OTCi - OTClient improvement project

Yamaken

Pro OpenTibia Developer
Premium User
Joined
Jul 27, 2013
Messages
457
Reaction score
327
Hi guys, I am working with two very experienced programmers to help me solve the problem. After three weeks of examining the project, they have determined that all graphic management is a real disaster. It uses prehistoric methods and almost completely obsolete. Use the version of openGL 3.0, which is very old! They tell me that it would be faster to directly change the graphic engine of the game, which is a huge job, of more than a year which entails a very high economic investment. Repair the current graphic engine is patching a code badly worked, very messy and patched. It is patching the patched. In short, otclient is not viable. I hope you understand my bad English.
Did you come from the future?
 

Buttface Donkey

Godly Donkey
Premium User
Joined
Dec 14, 2009
Messages
21
Reaction score
39
Use the version of openGL 3.0, which is very old!
There is a reason for that
Copy paste from https://www.reddit.com/r/gamedev/comments/1dkjdt "The data says that around 90% of your audience has access to version 2.1 or above, 60% has access to 3.0 or above, and only 20% has access to 4.0 or above. As such, unless you really need tessellation or instancing or something, you should probably target version 2.1."

Although the post is 5 years old, a decent amount of people who play tibia today still use toasters.
With that in mind I wouldn't recommend upgrading the code with anything newer than OpenGL 3.3
 

Dual Core

New Member
Joined
Oct 25, 2018
Messages
11
Reaction score
8
OpenGL 3.0 uses very primitive functions. If we want to do something right, the worst we can think of is going back in time, technology advances. There are much more powerful and viable engines and above all much easier to manipulate, such as Vulkan or Unity. You could also optimize the current version 3.0, but believe me, that the current Otclient code is a real disaster. Very bad organization and really bad and feasible logics to generate future problems difficult to find and repair.
 

Arkam

Excellent OT User
Joined
May 1, 2009
Messages
192
Reaction score
38
nvm, dont like it
 
Last edited:

TheRealMystic

Aros.Online
Joined
Oct 30, 2007
Messages
283
Reaction score
26
Location
USA
I would be intrigued by this. I could make a donation myself, however here is the issues I foresee..

1 The files not being shared.
2 You taking the money and running.
3 Is the guys doing the job as good as we have here.
4 Who's to say they don't require a larger amount in the end.
5 If and when you release it are you going to release all of the files and not sell the fixed ones.

These are just a couple things I see going side ways. However they all could be cleared up by introducing your contact from this company, along with a agreement between them and you/us.

If you could provide me enough accurate, and solid facts with some sort of guaranteed agreement I would pay the remaining balance in full once it is completed. You already have many people willing to back you with more reputation than me... So what's the hold up in providing proof of what's happening?
 

Ahilphino

Veteran OT User
Joined
Jun 5, 2013
Messages
1,367
Reaction score
400
hello 4drik u said u working on fixing animations too
ninja uploaded fix for idle animations and it works good so dont need working on this
i compiled and tested this client (otland/otclient (https://github.com/otland/otclient/tree/WalkAnimation))
he also uploaded some changes to walking animations. i think his walking animations are better than the default ones but they are way too quick (player moves feet very quickly when moving)
so some further improvements could be done on this, since regular tibia client still feels better in this. but this is a nice start.
 

Madzix

Member
Joined
Sep 8, 2016
Messages
61
Reaction score
65
About OpenGL - yeah it sucks it uses a lot more CPU than Direct3D especially on amd drivers + I'm hate the approach with extensions which in some cases is a lottery to have them.
Here's a little example on a client that I'm working whenever I have time(not OTClient):
OpenGL -
34435
Vulkan -
34437

As you can see the Vulkan is about 75% faster because of better CPU utilization(It's my first attempt with Vulkan while I worked with OpenGL for some time) and at this moment the OpenGL code part are much more optimized than Vulkan(Direct3D is about 40-50% faster than OpenGL - just to say I'm talking about the same optimization level, it's not that the corporations are bought by Microsoft to make games for Directx it just that it's better and not talking about emulating Directx via Angle because it'll always be much slower).

About performance issue with monsters on battle list is probably how the "outfits" are drawing on the list - "outfitBuffer->resize(Size(frameSize, frameSize));" the framebuffer most likely will get resized multiple time per frame which is taking some time because it need to recreate texture and reattach the fbo with checking for success - glCheckFramebufferStatus is taking a lot of time for checking if the fbo gets attached successfully.

About the Edubart's points:
-"1. Drawing Less Tiles " - of course it will reduce the time rendering takes but there are somethings to consider - when moving you still need to draw these extra tiles + what if there're some things that takes more than 1x1 tile? It most likely will result in some kinds of "glitches".
-"2. Drawing Even Less Tiles " - like before it can result in glitches because the function "isCompletelyCovered" does not check for objects that takes more tiles + this function probably will be much more costly than simple drawcall unless you use really-really crappy old GPU.
-"5. Reduce Texture Binding" - you should consider this point the most because from my experience texture atlas gives you about 40% performance boost on OpenGL and even to 70% on Direct3D.

And please don't ask me to take part in this because I'll decline just because I don't like the OTClient itself.
 
OP
4drik

4drik

Active Member
Joined
Jun 30, 2014
Messages
189
Reaction score
138
Unfortunately, the project was not welcomed by the community, therefore I finish the work on it.
In the meantime I wrote idle animation system from scratch, I added possibility to edit outfit properties from the notepad, like bars position, flying animation, centered outfit, example:
Code:
outfit
  715
    bars-offset-y: 15
    idle-animation-frames: 2
    center: true

I worked also on APNGLoader and upgraded it to APNG Disassembler 2.5 from 2.3.
If you want it, there it is: APNG Disassembler - Browse /2.5 at SourceForge.net (https://sourceforge.net/projects/apngdis/files/2.5/)
I cached LuaObject::getClassName().

Thank you for helping those who helped.
I am also sending you the first point from the Edubart's list:

In map.h under:
C++:
bool isCovered(const Position& pos, int firstFloor = 0);
Paste:
C++:
bool posIsInRange(const Position& pos);
In map.cpp under a function:
C++:
bool Map::isCovered(const Position& pos, int firstFloor)
Paste:

C++:
bool Map::posIsInRange(const Position& pos)
{
    bool inRange = true;
    LocalPlayerPtr localPlayer = g_game.getLocalPlayer();
    if(localPlayer){
        Position localPos = localPlayer->getPosition();
        int dz = pos.z - localPos.z;
        Position checkPos = pos.translated(dz, dz);
        const TilePtr& borderTile = getTile(pos);
        bool outOfRangeY = false;
        if(abs(checkPos.y - localPos.y) > Otc::RANGE_TOP){
            outOfRangeY = true;
        }
        bool outOfRangeX = false;
        if(abs(checkPos.x - localPos.x) > Otc::RANGE_LEFT){
            outOfRangeX = true;
        }
        if(!localPlayer->isWalking()){
            if(outOfRangeY || outOfRangeX){
                if((((checkPos.y > localPos.y) && !outOfRangeX) || checkPos.x > localPos.x)){
                    if(borderTile->isSingleDimension()){
                        inRange = false;
                    }
                }
                else
                {
                    inRange = false;
                }
            }
        }
        else
        {
            Otc::Direction dir = localPlayer->getDirection();
            switch(dir) {
                case Otc::North:
                    if(outOfRangeX){
                        if(checkPos.x > localPos.x){
                            if(borderTile->isSingleDimension()){
                                inRange = false;
                            }
                        }
                        else
                        {
                            inRange = false;
                        }
                    }
                    else if(((checkPos.y - localPos.y) > Otc::RANGE_TOP) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    break;
                case Otc::South:
                    if(outOfRangeX){
                        if(checkPos.x > localPos.x){
                            if(borderTile->isSingleDimension()){
                                inRange = false;
                            }
                        }
                        else
                        {
                            inRange = false;
                        }
                    }
                    else if((localPos.y - checkPos.y) > Otc::RANGE_TOP){
                        inRange = false;
                    }
                    break;
                case Otc::West:
                    if(outOfRangeY){
                        if(((checkPos.y > localPos.y) && !outOfRangeX)){
                            if(borderTile->isSingleDimension()){
                                inRange = false;
                            }
                        }
                        else
                        {
                            inRange = false;
                        }
                    }
                    else if(((checkPos.x - localPos.x) > Otc::RANGE_LEFT) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    break;
                case Otc::East:
                    if(outOfRangeY){
                        if(((checkPos.y > localPos.y) && !outOfRangeX)){
                            if(borderTile->isSingleDimension()){
                                inRange = false;
                            }
                        }
                        else
                        {
                            inRange = false;
                        }
                    }
                    else if((localPos.x - checkPos.x) > Otc::RANGE_LEFT){
                        inRange = false;
                    }
                    break;
                case Otc::NorthEast:
                    if(((checkPos.y - localPos.y) > Otc::RANGE_TOP) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    else if((localPos.x - checkPos.x) > Otc::RANGE_LEFT){
                        inRange = false;
                    }
                    break;
                case Otc::SouthEast:
                    if((localPos.y - checkPos.y) > Otc::RANGE_TOP){
                        inRange = false;
                    }
                    else if((localPos.x - checkPos.x) > Otc::RANGE_LEFT){
                        inRange = false;
                    }
                    break;
                case Otc::SouthWest:
                    if((localPos.y - checkPos.y) > Otc::RANGE_TOP){
                        inRange = false;
                    }
                    else if(((checkPos.x - localPos.x) > Otc::RANGE_LEFT) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    break;
                case Otc::NorthWest:
                    if(((checkPos.y - localPos.y) > Otc::RANGE_TOP) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    else if(((checkPos.x - localPos.x) > Otc::RANGE_LEFT) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    break;
                case Otc::InvalidDirection:
                    inRange = false;
                    break;
            }
        }
    }
    return inRange;
}
In mapview.cpp under:
C++:
                        // skip tiles that have nothing
                        if(!tile->isDrawable())
                            continue;
Paste:
C++:
                        // skip tiles that are not in our range
                        if(!g_map.posIsInRange(tilePos))
                            continue;
In const.h under:
C++:
        TILE_PIXELS = 32,
        MAX_ELEVATION = 24,
Paste:
C++:
        RANGE_LEFT = 8,
        RANGE_TOP = 6,
In map.cpp the function:
C++:
void Map::resetAwareRange()
Change to:
C++:
void Map::resetAwareRange()
{
    AwareRange range;
    range.left = Otc::RANGE_LEFT;
    range.top = Otc::RANGE_TOP;
    range.bottom = range.top + 1;
    range.right = range.left + 1;
    setAwareRange(range);
}
Best regards,
Adrik
 

AngeLOT

Active Member
Joined
Dec 14, 2017
Messages
453
Reaction score
76
Unfortunately, the project was not welcomed by the community, therefore I finish the work on it.
In the meantime I wrote idle animation system from scratch, I added possibility to edit outfit properties from the notepad, like bars position, flying animation, centered outfit, example:
Code:
outfit
  715
    bars-offset-y: 15
    idle-animation-frames: 2
    center: true

I worked also on APNGLoader and upgraded it to APNG Disassembler 2.5 from 2.3.
If you want it, there it is: APNG Disassembler - Browse /2.5 at SourceForge.net (https://sourceforge.net/projects/apngdis/files/2.5/)
I cached LuaObject::getClassName().

Thank you for helping those who helped.
I am also sending you the first point from the Edubart's list:

In map.h under:
C++:
bool isCovered(const Position& pos, int firstFloor = 0);
Paste:
C++:
bool posIsInRange(const Position& pos);
In map.cpp under a function:
C++:
bool Map::isCovered(const Position& pos, int firstFloor)
Paste:

C++:
bool Map::posIsInRange(const Position& pos)
{
    bool inRange = true;
    LocalPlayerPtr localPlayer = g_game.getLocalPlayer();
    if(localPlayer){
        Position localPos = localPlayer->getPosition();
        int dz = pos.z - localPos.z;
        Position checkPos = pos.translated(dz, dz);
        const TilePtr& borderTile = getTile(pos);
        bool outOfRangeY = false;
        if(abs(checkPos.y - localPos.y) > Otc::RANGE_TOP){
            outOfRangeY = true;
        }
        bool outOfRangeX = false;
        if(abs(checkPos.x - localPos.x) > Otc::RANGE_LEFT){
            outOfRangeX = true;
        }
        if(!localPlayer->isWalking()){
            if(outOfRangeY || outOfRangeX){
                if((((checkPos.y > localPos.y) && !outOfRangeX) || checkPos.x > localPos.x)){
                    if(borderTile->isSingleDimension()){
                        inRange = false;
                    }
                }
                else
                {
                    inRange = false;
                }
            }
        }
        else
        {
            Otc::Direction dir = localPlayer->getDirection();
            switch(dir) {
                case Otc::North:
                    if(outOfRangeX){
                        if(checkPos.x > localPos.x){
                            if(borderTile->isSingleDimension()){
                                inRange = false;
                            }
                        }
                        else
                        {
                            inRange = false;
                        }
                    }
                    else if(((checkPos.y - localPos.y) > Otc::RANGE_TOP) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    break;
                case Otc::South:
                    if(outOfRangeX){
                        if(checkPos.x > localPos.x){
                            if(borderTile->isSingleDimension()){
                                inRange = false;
                            }
                        }
                        else
                        {
                            inRange = false;
                        }
                    }
                    else if((localPos.y - checkPos.y) > Otc::RANGE_TOP){
                        inRange = false;
                    }
                    break;
                case Otc::West:
                    if(outOfRangeY){
                        if(((checkPos.y > localPos.y) && !outOfRangeX)){
                            if(borderTile->isSingleDimension()){
                                inRange = false;
                            }
                        }
                        else
                        {
                            inRange = false;
                        }
                    }
                    else if(((checkPos.x - localPos.x) > Otc::RANGE_LEFT) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    break;
                case Otc::East:
                    if(outOfRangeY){
                        if(((checkPos.y > localPos.y) && !outOfRangeX)){
                            if(borderTile->isSingleDimension()){
                                inRange = false;
                            }
                        }
                        else
                        {
                            inRange = false;
                        }
                    }
                    else if((localPos.x - checkPos.x) > Otc::RANGE_LEFT){
                        inRange = false;
                    }
                    break;
                case Otc::NorthEast:
                    if(((checkPos.y - localPos.y) > Otc::RANGE_TOP) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    else if((localPos.x - checkPos.x) > Otc::RANGE_LEFT){
                        inRange = false;
                    }
                    break;
                case Otc::SouthEast:
                    if((localPos.y - checkPos.y) > Otc::RANGE_TOP){
                        inRange = false;
                    }
                    else if((localPos.x - checkPos.x) > Otc::RANGE_LEFT){
                        inRange = false;
                    }
                    break;
                case Otc::SouthWest:
                    if((localPos.y - checkPos.y) > Otc::RANGE_TOP){
                        inRange = false;
                    }
                    else if(((checkPos.x - localPos.x) > Otc::RANGE_LEFT) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    break;
                case Otc::NorthWest:
                    if(((checkPos.y - localPos.y) > Otc::RANGE_TOP) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    else if(((checkPos.x - localPos.x) > Otc::RANGE_LEFT) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    break;
                case Otc::InvalidDirection:
                    inRange = false;
                    break;
            }
        }
    }
    return inRange;
}
In mapview.cpp under:
C++:
                        // skip tiles that have nothing
                        if(!tile->isDrawable())
                            continue;
Paste:
C++:
                        // skip tiles that are not in our range
                        if(!g_map.posIsInRange(tilePos))
                            continue;
In const.h under:
C++:
        TILE_PIXELS = 32,
        MAX_ELEVATION = 24,
Paste:
C++:
        RANGE_LEFT = 8,
        RANGE_TOP = 6,
In map.cpp the function:
C++:
void Map::resetAwareRange()
Change to:
C++:
void Map::resetAwareRange()
{
    AwareRange range;
    range.left = Otc::RANGE_LEFT;
    range.top = Otc::RANGE_TOP;
    range.bottom = range.top + 1;
    range.right = range.left + 1;
    setAwareRange(range);
}
Best regards,
Adrik
This works on animated mounts aswell?
 

Sir Knighter

ArchlightOnline.com
Premium User
Joined
Jun 29, 2009
Messages
4,043
Reaction score
1,137
Location
Canada
Unfortunately, the project was not welcomed by the community, therefore I finish the work on it.
In the meantime I wrote idle animation system from scratch, I added possibility to edit outfit properties from the notepad, like bars position, flying animation, centered outfit, example:
Code:
outfit
  715
    bars-offset-y: 15
    idle-animation-frames: 2
    center: true

I worked also on APNGLoader and upgraded it to APNG Disassembler 2.5 from 2.3.
If you want it, there it is: APNG Disassembler - Browse /2.5 at SourceForge.net (https://sourceforge.net/projects/apngdis/files/2.5/)
I cached LuaObject::getClassName().

Thank you for helping those who helped.
I am also sending you the first point from the Edubart's list:

In map.h under:
C++:
bool isCovered(const Position& pos, int firstFloor = 0);
Paste:
C++:
bool posIsInRange(const Position& pos);
In map.cpp under a function:
C++:
bool Map::isCovered(const Position& pos, int firstFloor)
Paste:

C++:
bool Map::posIsInRange(const Position& pos)
{
    bool inRange = true;
    LocalPlayerPtr localPlayer = g_game.getLocalPlayer();
    if(localPlayer){
        Position localPos = localPlayer->getPosition();
        int dz = pos.z - localPos.z;
        Position checkPos = pos.translated(dz, dz);
        const TilePtr& borderTile = getTile(pos);
        bool outOfRangeY = false;
        if(abs(checkPos.y - localPos.y) > Otc::RANGE_TOP){
            outOfRangeY = true;
        }
        bool outOfRangeX = false;
        if(abs(checkPos.x - localPos.x) > Otc::RANGE_LEFT){
            outOfRangeX = true;
        }
        if(!localPlayer->isWalking()){
            if(outOfRangeY || outOfRangeX){
                if((((checkPos.y > localPos.y) && !outOfRangeX) || checkPos.x > localPos.x)){
                    if(borderTile->isSingleDimension()){
                        inRange = false;
                    }
                }
                else
                {
                    inRange = false;
                }
            }
        }
        else
        {
            Otc::Direction dir = localPlayer->getDirection();
            switch(dir) {
                case Otc::North:
                    if(outOfRangeX){
                        if(checkPos.x > localPos.x){
                            if(borderTile->isSingleDimension()){
                                inRange = false;
                            }
                        }
                        else
                        {
                            inRange = false;
                        }
                    }
                    else if(((checkPos.y - localPos.y) > Otc::RANGE_TOP) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    break;
                case Otc::South:
                    if(outOfRangeX){
                        if(checkPos.x > localPos.x){
                            if(borderTile->isSingleDimension()){
                                inRange = false;
                            }
                        }
                        else
                        {
                            inRange = false;
                        }
                    }
                    else if((localPos.y - checkPos.y) > Otc::RANGE_TOP){
                        inRange = false;
                    }
                    break;
                case Otc::West:
                    if(outOfRangeY){
                        if(((checkPos.y > localPos.y) && !outOfRangeX)){
                            if(borderTile->isSingleDimension()){
                                inRange = false;
                            }
                        }
                        else
                        {
                            inRange = false;
                        }
                    }
                    else if(((checkPos.x - localPos.x) > Otc::RANGE_LEFT) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    break;
                case Otc::East:
                    if(outOfRangeY){
                        if(((checkPos.y > localPos.y) && !outOfRangeX)){
                            if(borderTile->isSingleDimension()){
                                inRange = false;
                            }
                        }
                        else
                        {
                            inRange = false;
                        }
                    }
                    else if((localPos.x - checkPos.x) > Otc::RANGE_LEFT){
                        inRange = false;
                    }
                    break;
                case Otc::NorthEast:
                    if(((checkPos.y - localPos.y) > Otc::RANGE_TOP) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    else if((localPos.x - checkPos.x) > Otc::RANGE_LEFT){
                        inRange = false;
                    }
                    break;
                case Otc::SouthEast:
                    if((localPos.y - checkPos.y) > Otc::RANGE_TOP){
                        inRange = false;
                    }
                    else if((localPos.x - checkPos.x) > Otc::RANGE_LEFT){
                        inRange = false;
                    }
                    break;
                case Otc::SouthWest:
                    if((localPos.y - checkPos.y) > Otc::RANGE_TOP){
                        inRange = false;
                    }
                    else if(((checkPos.x - localPos.x) > Otc::RANGE_LEFT) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    break;
                case Otc::NorthWest:
                    if(((checkPos.y - localPos.y) > Otc::RANGE_TOP) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    else if(((checkPos.x - localPos.x) > Otc::RANGE_LEFT) && borderTile->isSingleDimension()){
                        inRange = false;
                    }
                    break;
                case Otc::InvalidDirection:
                    inRange = false;
                    break;
            }
        }
    }
    return inRange;
}
In mapview.cpp under:
C++:
                        // skip tiles that have nothing
                        if(!tile->isDrawable())
                            continue;
Paste:
C++:
                        // skip tiles that are not in our range
                        if(!g_map.posIsInRange(tilePos))
                            continue;
In const.h under:
C++:
        TILE_PIXELS = 32,
        MAX_ELEVATION = 24,
Paste:
C++:
        RANGE_LEFT = 8,
        RANGE_TOP = 6,
In map.cpp the function:
C++:
void Map::resetAwareRange()
Change to:
C++:
void Map::resetAwareRange()
{
    AwareRange range;
    range.left = Otc::RANGE_LEFT;
    range.top = Otc::RANGE_TOP;
    range.bottom = range.top + 1;
    range.right = range.left + 1;
    setAwareRange(range);
}
Best regards,
Adrik
The concept itself would of been welcomed... The way you handled it wasn't..
 

Sir Knighter

ArchlightOnline.com
Premium User
Joined
Jun 29, 2009
Messages
4,043
Reaction score
1,137
Location
Canada
Why if I can ask?
Several of us veterans (Including mods on this forum) asked for some specific details for transparency, he refused to give them (at times seems straight up ignored). As well, alot of details & information never added up (to give him benefit of the doubt, maybe they would have, if he was fully transparent, but he wasn't). That's not how you should try to convince the community to trust you with their money. Was a good idea, run by the wrong person.
 
OP
4drik

4drik

Active Member
Joined
Jun 30, 2014
Messages
189
Reaction score
138
I am asking moderators to close the topic. I’m not going go continue it and I do not expect anything related to the technical side of OTC then.

Adrik
 
Last edited:

Ochman

Premium User
Premium User
Joined
Feb 27, 2016
Messages
201
Reaction score
128
I am asking moderators to close the topic so that the Knighter's clown's stupidity will no longer spread.
Regardless of how this project is handled, you take this criticism way too personal, spread some insults left and right and act like a victim, which you are not. Then you expect others to respect you, where in fact you don't respect them either.
 
OP
4drik

4drik

Active Member
Joined
Jun 30, 2014
Messages
189
Reaction score
138
Regardless of how this project is handled, you take this criticism way too personal, spread some insults left and right and act like a victim, which you are not. Then you expect others to respect you, where in fact you don't respect them either.
I’m sorry. Fixed, take care.

Adrik
 
Last edited:
Top