Jump to content

masodikbela

Premium
  • Posts

    235
  • Joined

  • Last visited

  • Days Won

    28
  • Feedback

    100%

Posts posted by masodikbela

  1. However I can't blame gameforge because of everything. They are only a publisher of the game. So we have webzen as the developer, they decide what to code and what patch to give for the publishers. Publishers don't even have a say about it. So if webzen gives a patch with 6th 7th bonus, publishers can decide if they put the patch to live, or no. They can't undo this. They don't even have source or anything.

    I was doing a research some weeks ago if its possible to get a license from webzen, and if so will you have the right to change the game the way you would like to, and (I didn't talk to anybody nor contacted anybody, only read the available documents and stuff) I realized that no, its not possible. So currently we have 2 companies with license to metin according to webzen's homepage: Ongame (in brazil) and Gameforge (in europe).

    Yes, thats true that gameforge is still full of crap, and they are doing questionable things like this copyright strike, but still I can't blame them for everything. Not sure if they do or do not care about the feedbacks, or just webzen pisses off the players needs.

    • Love 2
  2. Okay, it feels like now you think I'm the bad ugly dev here, because I refused to share or sell this stuff here despite I having it for more than a year. Moreover you took my detailed review about this fix as a personal attack. Let me clear some stuff here (I hope you manage to chill by the time you are reading this).

    I wrote my answer without any hate or anything. Yes, like I said it felt a bit bad for not leaving any credit for the idea at least with a 1pt textsize that nobody can see but still there. My goal was to tell you the technical problems with your changes. I wrote nothing but the truth. Its always sad to see when someone ignores my technical advises but its more sad when someone misunderstands my intent. Truth sometimes painful but its always necessary. I could have write that "ahh man its even better than my solution, thank you very much, sorry for not telling you and releasing my version".  Would it help? Not sure. Did the stuff I do help? In long term I'm sure, but even in short term you changed that now all the motions load correctly, so I guess it did worth.

    You were asking in the first comment for help and discussion, to improve your solution. I was hoping that my comment will serve this purpose. I wrote even more detailed in some topics there because you said that you are not a programmer. (I would have wrote it anyway because I know that there are not much people out there knowing about pooling and stuff.)

    I've stopped releasing stuff long time ago, because it felt like giving fully ready stuff under people butts are moving the community and the devs in the wrong way. I've realized that there are no or not much professional devs out there because they are not trying to dive into deep inside stuff and they are doing it because of the money only. So I was thinking what could I do for this community to change this, or help changing this so we could get other more professional devs, and making some competition for the other pros out there. I came to this conclusion that if I explain stuff like this or stuff like the rect clippin would probably impel others to get some experience.

    There is another reason why I didn't published this particular one is that I made this for a server as a freelancer under a contract, which says I can't share or sell stuff I made for them.

     

    Also (and I'm writing this only to inform you, so no hate or anything still) that removing contents are forbidden on this forum.

    • Love 1
  3. The original topic got updated/changed over time but I will not update this comment, so its normal to feel confused while reading it.

    Spoiler

    OFF:
    I'm a bit disappointed... at least you could have mention where the idea come from, but whatever...

    I've run through the method you chose, and I have some notes about them.

    1. c_Reference_Decrease_Wait_Time is unused right now, because the client is not using other thread for background loading by default, so m_RequestMap will be empty always, so ms_loadingThread.Fetch(&pData) will return false always. Moreover, the whole ProcessBackgroundLoading function is unused right now, since it only do things if the mentioned background stuff is enabled.

    2. Editing g_iLoadingDelayTime in that file is probably pointless, since it will be overwritten by the config file. You should remove that option from the metin2.cfg.

    3. Editing __DestroyCacheMap is pointless aswell, since it will only run when you close the client. Also I would put a very very big red exclamation mark here, since you commented out m_pCacheMap.clear(). In theory (ofc it will never happen because the function runs only when you close the client so nobody will give a fck about that cache map anymore) this could lead to huge crashes since in CReferenceObject::Release a deallocation could happen (its possible that it won't, because its possible that at that point something is using that actual file for some reason, so the reference count will still be higher than 1). And after that since you don't clear the cache map, the next time when the system would try to use the cache map, it would receive a corrupt memory address, since we deallocated the object from that address.

      So anyway if you still want to make sure that nothing will call that function, just put a return to its beginning.

    4. About the last edit: I would put an even bigger red exclamation mark there. This requires some further explanation tho, so get ready for it.

      So first of all, let me talk about memory pools. So as you may know, our processors work better (talking about performance) if the memory addresses are close to each other. Also it works even better, if the affected memory block it needs to read is inside its cache. Also, allocating/deallocating memory runtime is an expensive operation. So long ago programmers came up with an idea: Lets allocate a big chunk of memory when we start our program, so we can put our instances inside it, so the data will be in one place close to each other, and we don't even have to bother with allocation and deallocation. So memory pooling was born.

      Now that we know this, lets talk about the implementation of CDynamicPool. So as you can see, we have 2 vectors inside it. m_kVct_pkData and m_kVct_pkFree. The first one contains all the ADDRESSES of the given object's instances (even if they are not used at the moment). The second one contains ADDRESSES of those objects that are no longer in use. (As you can see it contains ADDRESSES, so this type of implementation does not solve the problem with the "data in one place" problem, because its possible that we will get an address far far away from the previous one, but its irrelevant right now.) If you take a closer look, you will see new operator, but you won't see delete operator. (This is a possible memory leak factor tho, but since you don't remove those memory addresses from the union of those two vectors, its not the case in reality.) Moreover, if you check the Free function, you will only see that we just simply put an address to the pkFree vector. It means, that the destructor of the given object will not get called. (This could and probably do (in some cases) lead some actual memory leaks, but again, this is not what I want to talk about right now.)

      Okay, now lets say that we have 2 allocated instances, so we have 2 already-in-use addresses in pkData vector. Lets say that one of them gets "deleted". The Free function will put its address to the pkFree vector. Then, we say that uhhhh ummmmm wait wait, I need a new instance. Okay, we call Alloc. Lets see what will happen: pkFree is not empty, so it will not allocate a new instance, just simply gives the last free address back from pkFree. Looks safe and simple right? Yes, thats the case if you are aware that NO CONSTRUCTOR WILL BE CALLED THERE. Whats the problem with it you may ask... The problem is that we didn't change the content of that instance during this Free and Alloc call. (Sure if we've called some cleanup function outside of the CDynamicCache this is not the case like I said, but then it means you were aware of this problem.)

      So what you did when you commented out that pMotionModeData->MotionVectorMap.clear(); part was removing this correct cleanup. So what could happen (at least in theory)? Lets say we have a male warrior. This male warrior go away and all the data gets deleted after 4 hours, and you just simply stand still and no other characters appear. The game will remove the unused motions from the CDynamicCache. Then suddenly out of nowhere someone summons a wild dog. It will receive the same exact stuff from the pkFree vector that we just removed. So that wild dog will have 240+ motions loaded instead of 4 or something like that.

      Sadly we can state that our new wild dog won't run with the male warrior's animation, since in NEW_RegisterMotion that animation will be overwritten, so there will be no visual defects.

    5. I'm not sure about the chrmgrRegisterMotionData changes, but probably those don't add much to the final result. My opinion is that changing c_Deleting_Wait_Time to a much higher value was the only thing you did and actually helps in this problem.

      So basically what you did was: You changed that the unused files don't get deleted over time (or just after 4 hours), which means that in this specific case if you go to somewhere alone, and no other player appears for 4 hours (instead of the original 30 second) and after that someone with other type of character (so you are a warrior and the other player is not a warrior) appears, you will still have the same lag like before.

    Conclusion:

    Disregarding the wrong/useless parts, it doesn't solve the main problem. In the gif you shared its clearly visible, that not all the motions load during the loading screen. Example (what you should see) for female assassin (241 motions in total):

    Spoiler
    
    d:\ymir work\pc\assassin\general\wait.gr2
    d:\ymir work\pc\assassin\general\wait_1.gr2
    d:\ymir work\pc\assassin\general\walk.gr2
    d:\ymir work\pc\assassin\general\run.gr2
    d:\ymir work\pc\assassin\general\damage.gr2
    d:\ymir work\pc\assassin\general\damage_1.gr2
    d:\ymir work\pc\assassin\general\damage_flying.gr2
    d:\ymir work\pc\assassin\general\falling_stand.gr2
    d:\ymir work\pc\assassin\general\damage_2.gr2
    d:\ymir work\pc\assassin\general\damage_3.gr2
    d:\ymir work\pc\assassin\general\back_damage_flying.gr2
    d:\ymir work\pc\assassin\general\back_falling_stand.gr2
    d:\ymir work\pc\assassin\general\dead.gr2
    d:\ymir work\pc\assassin\general\attack.gr2
    d:\ymir work\pc\assassin\general\attack_1.gr2
    d:\ymir work\pc\assassin\intro\wait.gr2
    d:\ymir work\pc\assassin\intro\selected.gr2
    d:\ymir work\pc\assassin\intro\not_selected.gr2
    d:\ymir work\pc\assassin\skill\amseup.gr2
    d:\ymir work\pc\assassin\skill\gungsin.gr2
    d:\ymir work\pc\assassin\skill\charyun.gr2
    d:\ymir work\pc\assassin\skill\eunhyeong.gr2
    d:\ymir work\pc\assassin\skill\poison_cloud.gr2
    d:\ymir work\pc\assassin\skill\seomjeon.gr2
    d:\ymir work\pc\assassin\skill\gwangyeok.gr2
    d:\ymir work\pc\assassin\skill\gwangyeok.gr2
    d:\ymir work\pc\assassin\skill\shoot.gr2
    d:\ymir work\pc\assassin\skill\gyeonggong.gr2
    d:\ymir work\pc\assassin\skill\shoot.gr2
    d:\ymir work\pc\assassin\skill\seomgwang.gr2
    d:\ymir work\pc\assassin\skill\amseup.gr2
    d:\ymir work\pc\assassin\skill\gungsin.gr2
    d:\ymir work\pc\assassin\skill\charyun.gr2
    d:\ymir work\pc\assassin\skill\eunhyeong.gr2
    d:\ymir work\pc\assassin\skill\poison_cloud.gr2
    d:\ymir work\pc\assassin\skill\seomjeon.gr2
    d:\ymir work\pc\assassin\skill\gwangyeok.gr2
    d:\ymir work\pc\assassin\skill\gwangyeok.gr2
    d:\ymir work\pc\assassin\skill\shoot.gr2
    d:\ymir work\pc\assassin\skill\gyeonggong.gr2
    d:\ymir work\pc\assassin\skill\shoot.gr2
    d:\ymir work\pc\assassin\skill\seomgwang.gr2
    d:\ymir work\pc\assassin\skill\amseup.gr2
    d:\ymir work\pc\assassin\skill\gungsin.gr2
    d:\ymir work\pc\assassin\skill\charyun.gr2
    d:\ymir work\pc\assassin\skill\eunhyeong.gr2
    d:\ymir work\pc\assassin\skill\poison_cloud.gr2
    d:\ymir work\pc\assassin\skill\seomjeon.gr2
    d:\ymir work\pc\assassin\skill\gwangyeok.gr2
    d:\ymir work\pc\assassin\skill\gwangyeok.gr2
    d:\ymir work\pc\assassin\skill\shoot.gr2
    d:\ymir work\pc\assassin\skill\gyeonggong.gr2
    d:\ymir work\pc\assassin\skill\shoot.gr2
    d:\ymir work\pc\assassin\skill\seomgwang.gr2
    d:\ymir work\pc\assassin\skill\amseup.gr2
    d:\ymir work\pc\assassin\skill\gungsin.gr2
    d:\ymir work\pc\assassin\skill\charyun.gr2
    d:\ymir work\pc\assassin\skill\eunhyeong.gr2
    d:\ymir work\pc\assassin\skill\poison_cloud.gr2
    d:\ymir work\pc\assassin\skill\seomjeon.gr2
    d:\ymir work\pc\assassin\skill\gwangyeok.gr2
    d:\ymir work\pc\assassin\skill\gwangyeok.gr2
    d:\ymir work\pc\assassin\skill\shoot.gr2
    d:\ymir work\pc\assassin\skill\gyeonggong.gr2
    d:\ymir work\pc\assassin\skill\shoot.gr2
    d:\ymir work\pc\assassin\skill\seomgwang.gr2
    d:\ymir work\pc\assassin\skill\amseup.gr2
    d:\ymir work\pc\assassin\skill\kwaegeom.gr2
    d:\ymir work\pc\assassin\skill\gungsin.gr2
    d:\ymir work\pc\assassin\skill\kwaegeom.gr2
    d:\ymir work\pc\assassin\skill\charyun.gr2
    d:\ymir work\pc\assassin\skill\kwaegeom.gr2
    d:\ymir work\pc\assassin\skill\eunhyeong.gr2
    d:\ymir work\pc\assassin\skill\kwaegeom.gr2
    d:\ymir work\pc\assassin\skill\poison_cloud.gr2
    d:\ymir work\pc\assassin\skill\kwaegeom.gr2
    d:\ymir work\pc\assassin\skill\seomjeon.gr2
    d:\ymir work\pc\assassin\skill\kwaegeom.gr2
    d:\ymir work\pc\assassin\skill\gwangyeok.gr2
    d:\ymir work\pc\assassin\skill\gwangyeok.gr2
    d:\ymir work\pc\assassin\skill\shoot.gr2
    d:\ymir work\pc\assassin\skill\gyeonggong.gr2
    d:\ymir work\pc\assassin\skill\shoot.gr2
    d:\ymir work\pc\assassin\skill\seomgwang.gr2
    d:\ymir work\pc\assassin\action\clap.gr2
    d:\ymir work\pc\assassin\action\cheers_1.gr2
    d:\ymir work\pc\assassin\action\cheers_2.gr2
    d:\ymir work\pc\assassin\action\kiss_with_warrior.gr2
    d:\ymir work\pc\assassin\action\kiss_with_assassin.gr2
    d:\ymir work\pc\assassin\action\kiss_with_sura.gr2
    d:\ymir work\pc\assassin\action\kiss_with_shaman.gr2
    d:\ymir work\pc\assassin\action\french_kiss_with_warrior.gr2
    d:\ymir work\pc\assassin\action\french_kiss_with_assassin.gr2
    d:\ymir work\pc\assassin\action\french_kiss_with_sura.gr2
    d:\ymir work\pc\assassin\action\french_kiss_with_shaman.gr2
    d:\ymir work\pc\assassin\action\slap_hit.gr2
    d:\ymir work\pc\assassin\action\slap_hit.gr2
    d:\ymir work\pc\assassin\action\slap_hit.gr2
    d:\ymir work\pc\assassin\action\slap_hit.gr2
    d:\ymir work\pc\assassin\action\slap_hurt.gr2
    d:\ymir work\pc\assassin\action\slap_hurt.gr2
    d:\ymir work\pc\assassin\action\slap_hurt.gr2
    d:\ymir work\pc\assassin\action\slap_hurt.gr2
    d:\ymir work\pc\assassin\general\dig.gr2
    d:\ymir work\pc\assassin\action\dance_1.gr2
    d:\ymir work\pc\assassin\action\dance_2.gr2
    d:\ymir work\pc\assassin\action\dance_3.gr2
    d:\ymir work\pc\assassin\action\dance_4.gr2
    d:\ymir work\pc\assassin\action\dance_5.gr2
    d:\ymir work\pc\assassin\action\congratulation.gr2
    d:\ymir work\pc\assassin\action\forgive.gr2
    d:\ymir work\pc\assassin\action\angry.gr2
    d:\ymir work\pc\assassin\action\attractive.gr2
    d:\ymir work\pc\assassin\action\sad.gr2
    d:\ymir work\pc\assassin\action\shy.gr2
    d:\ymir work\pc\assassin\action\cheerup.gr2
    d:\ymir work\pc\assassin\action\banter.gr2
    d:\ymir work\pc\assassin\action\joy.gr2
    d:\ymir work\pc\assassin\action\selfie.gr2
    d:\ymir work\pc\assassin\action\pushup.gr2
    d:\ymir work\pc\assassin\action\doze.gr2
    d:\ymir work\pc\assassin\action\exercise.gr2
    d:\ymir work\pc\assassin\action\dance_7.gr2
    d:\ymir work\pc\assassin\onehand_sword\wait.gr2
    d:\ymir work\pc\assassin\onehand_sword\wait_1.gr2
    d:\ymir work\pc\assassin\onehand_sword\walk.gr2
    d:\ymir work\pc\assassin\onehand_sword\run.gr2
    d:\ymir work\pc\assassin\onehand_sword\damage.gr2
    d:\ymir work\pc\assassin\onehand_sword\damage_1.gr2
    d:\ymir work\pc\assassin\onehand_sword\damage_2.gr2
    d:\ymir work\pc\assassin\onehand_sword\damage_3.gr2
    d:\ymir work\pc\assassin\onehand_sword\combo_01.gr2
    d:\ymir work\pc\assassin\onehand_sword\combo_02.gr2
    d:\ymir work\pc\assassin\onehand_sword\combo_03.gr2
    d:\ymir work\pc\assassin\onehand_sword\combo_04.gr2
    d:\ymir work\pc\assassin\onehand_sword\combo_05.gr2
    d:\ymir work\pc\assassin\onehand_sword\combo_06.gr2
    d:\ymir work\pc\assassin\onehand_sword\combo_07.gr2
    d:\ymir work\pc\assassin\dualhand_sword\wait.gr2
    d:\ymir work\pc\assassin\dualhand_sword\wait_1.gr2
    d:\ymir work\pc\assassin\dualhand_sword\walk.gr2
    d:\ymir work\pc\assassin\dualhand_sword\run.gr2
    d:\ymir work\pc\assassin\dualhand_sword\damage.gr2
    d:\ymir work\pc\assassin\dualhand_sword\damage_1.gr2
    d:\ymir work\pc\assassin\dualhand_sword\damage_2.gr2
    d:\ymir work\pc\assassin\dualhand_sword\damage_3.gr2
    d:\ymir work\pc\assassin\dualhand_sword\combo_01.gr2
    d:\ymir work\pc\assassin\dualhand_sword\combo_02.gr2
    d:\ymir work\pc\assassin\dualhand_sword\combo_03.gr2
    d:\ymir work\pc\assassin\dualhand_sword\combo_04.gr2
    d:\ymir work\pc\assassin\dualhand_sword\combo_05.gr2
    d:\ymir work\pc\assassin\dualhand_sword\combo_06.gr2
    d:\ymir work\pc\assassin\dualhand_sword\combo_07.gr2
    d:\ymir work\pc\assassin\dualhand_sword\combo_08.gr2
    d:\ymir work\pc\assassin\bow\wait.gr2
    d:\ymir work\pc\assassin\bow\wait_1.gr2
    d:\ymir work\pc\assassin\bow\walk.gr2
    d:\ymir work\pc\assassin\bow\run.gr2
    d:\ymir work\pc\assassin\bow\damage.gr2
    d:\ymir work\pc\assassin\bow\damage_1.gr2
    d:\ymir work\pc\assassin\bow\damage_2.gr2
    d:\ymir work\pc\assassin\bow\damage_3.gr2
    d:\ymir work\pc\assassin\bow\attack.gr2
    d:\ymir work\pc\assassin\fishing\wait.gr2
    d:\ymir work\pc\assassin\fishing\walk.gr2
    d:\ymir work\pc\assassin\fishing\run.gr2
    d:\ymir work\pc\assassin\fishing\throw.gr2
    d:\ymir work\pc\assassin\fishing\fishing_wait.gr2
    d:\ymir work\pc\assassin\fishing\fishing_cancel.gr2
    d:\ymir work\pc\assassin\fishing\fishing_wait.gr2
    d:\ymir work\pc\assassin\fishing\fishing_catch.gr2
    d:\ymir work\pc\assassin\fishing\fishing_fail.gr2
    d:\ymir work\pc\assassin\horse\wait.gr2
    d:\ymir work\pc\assassin\horse\wait_1.gr2
    d:\ymir work\pc\assassin\horse\wait_2.gr2
    d:\ymir work\pc\assassin\horse\walk.gr2
    d:\ymir work\pc\assassin\horse\run.gr2
    d:\ymir work\pc\assassin\horse\damage.gr2
    d:\ymir work\pc\assassin\horse\damage.gr2
    d:\ymir work\pc\assassin\horse\dead.gr2
    d:\ymir work\pc\assassin\horse\skill_charge.gr2
    d:\ymir work\pc\assassin\horse_onehand_sword\combo_01.gr2
    d:\ymir work\pc\assassin\horse_onehand_sword\combo_02.gr2
    d:\ymir work\pc\assassin\horse_onehand_sword\combo_03.gr2
    d:\ymir work\pc\assassin\horse_onehand_sword\skill_wildattack.gr2
    d:\ymir work\pc\assassin\horse_onehand_sword\skill_splash.gr2
    d:\ymir work\pc\assassin\horse_dualhand_sword\combo_01.gr2
    d:\ymir work\pc\assassin\horse_dualhand_sword\combo_02.gr2
    d:\ymir work\pc\assassin\horse_dualhand_sword\combo_03.gr2
    d:\ymir work\pc\assassin\horse_dualhand_sword\skill_wildattack.gr2
    d:\ymir work\pc\assassin\horse_onehand_sword\skill_splash.gr2
    d:\ymir work\pc\assassin\horse_bow\wait.gr2
    d:\ymir work\pc\assassin\horse_bow\wait_1.gr2
    d:\ymir work\pc\assassin\horse_bow\wait_2.gr2
    d:\ymir work\pc\assassin\horse_bow\run.gr2
    d:\ymir work\pc\assassin\horse_bow\damage.gr2
    d:\ymir work\pc\assassin\horse_bow\dead.gr2
    d:\ymir work\pc\assassin\horse_bow\attack.gr2
    d:\ymir work\pc\assassin\horse_bow\attack.gr2
    d:\ymir work\pc\assassin\horse_onehand_sword\skill_splash.gr2
    d:\ymir work\pc\assassin\wedding\wait.gr2
    d:\ymir work\pc\assassin\wedding\walk.gr2
    d:\ymir work\pc\assassin\wedding\walk.gr2
    d:\ymir work\pc\assassin\action\clap.gr2
    d:\ymir work\pc\assassin\action\cheers_1.gr2
    d:\ymir work\pc\assassin\action\cheers_2.gr2
    d:\ymir work\pc\assassin\action\kiss_with_warrior.gr2
    d:\ymir work\pc\assassin\action\kiss_with_assassin.gr2
    d:\ymir work\pc\assassin\action\kiss_with_sura.gr2
    d:\ymir work\pc\assassin\action\kiss_with_shaman.gr2
    d:\ymir work\pc\assassin\action\french_kiss_with_warrior.gr2
    d:\ymir work\pc\assassin\action\french_kiss_with_assassin.gr2
    d:\ymir work\pc\assassin\action\french_kiss_with_sura.gr2
    d:\ymir work\pc\assassin\action\french_kiss_with_shaman.gr2
    d:\ymir work\pc\assassin\action\slap_hit.gr2
    d:\ymir work\pc\assassin\action\slap_hit.gr2
    d:\ymir work\pc\assassin\action\slap_hit.gr2
    d:\ymir work\pc\assassin\action\slap_hit.gr2
    d:\ymir work\pc\assassin\action\slap_hurt.gr2
    d:\ymir work\pc\assassin\action\slap_hurt.gr2
    d:\ymir work\pc\assassin\action\slap_hurt.gr2
    d:\ymir work\pc\assassin\action\slap_hurt.gr2
    d:\ymir work\pc\assassin\action\dance_1.gr2
    d:\ymir work\pc\assassin\action\dance_2.gr2
    d:\ymir work\pc\assassin\action\dance_3.gr2
    d:\ymir work\pc\assassin\action\dance_4.gr2
    d:\ymir work\pc\assassin\action\dance_5.gr2
    d:\ymir work\pc\assassin\action\congratulation.gr2
    d:\ymir work\pc\assassin\action\forgive.gr2
    d:\ymir work\pc\assassin\action\angry.gr2
    d:\ymir work\pc\assassin\action\attractive.gr2
    d:\ymir work\pc\assassin\action\sad.gr2
    d:\ymir work\pc\assassin\action\shy.gr2
    d:\ymir work\pc\assassin\action\cheerup.gr2
    d:\ymir work\pc\assassin\action\banter.gr2
    d:\ymir work\pc\assassin\action\joy.gr2
    d:\ymir work\pc\assassin\action\selfie.gr2
    d:\ymir work\pc\assassin\action\pushup.gr2
    d:\ymir work\pc\assassin\action\doze.gr2
    d:\ymir work\pc\assassin\action\exercise.gr2
    d:\ymir work\pc\assassin\action\dance_7.gr2
    

    Because of this, when you login the first time, and you meet a different type of character, you will still have the same lag as before. (The first time I had some doubts in myself tho, so I tested this one to be sure, and I was right, so this is the case.)

    • Love 9
  4. 20 hours ago, ElRenardo said:

    Hi, thanks for your answer.

    I'm not familiar with this kind of things.

     

    It's like a library that you link to your project ?

    Any advice on what flags to use to get the best results ?

     

    I'll try that tomorrow aswell, thanks again.

    Its a built-in feature for clang, and if you are using clang you just have to pass a few more arguments for the build. (I think its working/integrated with gcc too, but I'm totally not sure about this since I always used clang and barely worked with gcc) I'm using a very default settings for ASAN, the related arguments are:

    CFLAGS += -fsanitize=address -fsanitize-recover=address -fno-omit-frame-pointer

    If you have ASAN enabled you must note that

    • it will use much much more memory (like 2-3 times as much as usually)
    • the program will always abort/crash on the first anomaly. Its necessary since if it continues there would be a chance that it would produce false results... this could be annoying sometimes if there are more bad parts in your code, cous it could happen that you want to fix x, but you have to fix y, z, i, j, k before, even if they are not related to your main problem.
    • the program will not generate core dump, instead it will write the result to the stderr, so you might want to run the program without vrunner and detour the output to a file (instead of writing it to the console)

    I can't help with valgrind since this is the first time I heard about it.

    @Sherer Thats not true for the affect pool. Its enabled by default, because its #ifndef DEBUG_ALLOC not #ifdef DEBUG_ALLOC.
    sT3k6mx.png

    • Love 2
  5. I would recommend you to try building your cores with ASAN (address sanitizer) enabled. I can't promise anything, but it can help you in desperate times... I remember having the weirdest crashes when AE opened. There was some heap-use-after-free error related to our multilanguage and battlepass systems that we would never been able to find without ASAN. (I don't think this is the case right here, just wanted to mention it as an example.) I think its worth a try. There are some nice article about it on the internet so I'm sure you can manage it, but if you have some questions about it I will try to answer them (despite I'm not an expert user of it).

    • Love 2
  6. Spoiler

     

    I guess this is what you are searching for. I recently used this technique in my new wiki (ignore the cursor, its position is fucked up in the gif): https://metin2.download/picture/KAC47a5f2jVYlnjMo28490u1Fsm4y11n/.gif
    The trick is exactly what you were thinking in your post above: First, you need a main window. We set this window's size to exactly as big as we need. Then lets make another window inside it, and make it as big as we need to store all elements we need in it. Then when you scroll we just have to move this inner window.

    But... you have to create a function (obviously this is the hard part of it) that handles the inner window. So basically it hides those parts of the window that is outside of the main window. I've seen some different implementation already, where they did this python side using the SetRenderingRect function, but I would avoid that (like I did) cus it would make the whole code a mess.

    Instead, I would recommend you to modify the render functions directly.
    What I did was:

    • created a new bool "isInsideRender" which I can set if the window need to check if its outside from its parent
    • created a RECT "render box" where I store how much is the current window is outside from its parent window
    • modified the necessary functions like set size, set position, etc, where I update the render box if the isInsideRender is set
    • modified the necessary windows (image box, textline) renders to render only the visible parts of the window according to the stored data in the render box

    Obviously I won't release it (sadly) like I didn't do it in the past ~1 year, but if you have some more questions about it (which is not like "can you show me the exact code") I'm here to help.

    • Love 3
  7. You should check if the thread is already joined, or just simply check if you already called Quit, cus db is crashing at the very end of the process, because quit is called from the db too (and also from the destructor of CAsyncSQL).
    NpEVRji.png

    5lM7g8a.png

    • Sad 1
  8. 10 minutes ago, rawn3cr0 said:

    @masodikbelaHow i can convert this system to save in sql table in db? no txt in quest

    You can't. Originally it was made this way because using sql queries in quest was too expensive... This whole thing is outdated right now so it would be better to refactor this system.

  9. You can disable the shadows cus as far as I saw that consumes the most... however the whole rendering thing is old and not well optimized, also it could be that I made mistakes in this solution and it could be improved too, but I think the real problem is the rendering itself... Btw the fps in metin is capped at 60 and I had somewhere between 40 and 60 with this on the original map1s(cus the map can also be the problem for your low fps).

    So in short: no I don't plan to improve it...

  10. 9 hours ago, PeaceMaker said:

    How are you supposed to make an option in gameoption if you deleted these functions 

     

    
    void CMapOutdoor::FPushTerrainToDeleteVector::operator () (CTerrain * pTerrain)
    void CMapOutdoor::FPushAreaToDeleteVector::operator () (CArea * pArea)
    void CMapOutdoor::__ClearGarvage()
    void CMapOutdoor::__UpdateGarvage()
    void CMapOutdoor::UpdateAreaList(long lCenterX, long lCenterY)
    

    Well if you do the first step, the client will load the whole map into the memory. This would increase the memory usage a little bit but would increase the performance too. Ofc even if the full render is disabled, this can't be disable because like you said I erased the garbage collector. But I think its not that big change, should not cause any problem even on low spec PCs, no need to worry about it.

    • Love 3
  11. 35 minutes ago, .T4Ump said:

    Will it stay in the cache? When several times teleport or every teleport again loading all? @masodikbela

    The loaded terrains and objects should be cleared when the bool CMapOutdoor::Destroy() is called, which should happen every time when you change map. If you want to make sure if its working as its intended, you can put a MessageBoxA into that function, and it will appear when its called.

    • Love 2
  12. Hi there devs,

    Okay, so after reading the title you may (or may not) have some thougts/questions like "Hollllllllly sh..", "Oooookay..." or just "?". About one or two years ago a mapper questioned me: "Is this possible, to make the whole map visible? Like in normal games? This fog is sooo disgusting, I've could create more beautiful view with a normal fog..." I've tried to do it many ways until finally after some sleepless night I've made it about a year ago. Once it was done I didn't know what to do with it. It was pretty good (I think) but since I'm not a mapper I can't do anything with it. I could have sell it, but since there is no way to protect this code its not an option for me, so I've decided to share it with the public. This TuT gonna be a bit long and there are several things to discuss but FIRST lets watch this video (in the first part I disabled the fog).

    "Wow, thats actually not bad" this was my first reaction when I first saw this view. BUT enough from me, lets talk about more important things...

    So I decided to create stages for this tutorial (4 stages actually). Its a normal down-top thing, so it means that you can do stage 1, 2 and then you can stop, but can't do stage 2 without 1, 3 without 2 and 4 without 3... it will help you to customize these modifications. Also I may write some "interesting" (at least for me) notes/thoughts for each stages. However, as you will see the last 2 stages are not ready at the moment, so I would rather say its an experimental release... Anyway, before we start to implement this lets talk about the tests, performance and other more or less important things.

    Table of content:

    • Stage 1: Removing garbage collectors, loading all map related things into memory ("chunks", objects, etc...)
    • Stage 2: Rendering full terrain
    • Stage 3: Rendering all objects on map [NOT READY YET]
    • Stage 4: Create config for players [NOT READY YET]

    Performance

    One of the most important questions about a game: will I be able to play it with my spec? Well of course this depends on much things. Using high poly objects, large maps, HD textures, lots of trees, effects can highly impact the performance/rendering time. So all I can say about it in a nutshell: its really depends on your maps (and of course the player's machine). Until now this old buddy could be ran on almost every computer but with this could change things so this is why I created config for it, enabling or disabling this feature.

    About my experiences/tests

    Well the memory usage increased, from the default ~270 MB to ~300 MB (see the explanations later), the processor usage didn't change (~5-10%). About my video card usage I can't tell nothing since I can't monitor it, but I'm sure its usage (both video memory and processing) increased too... You maybe noticed some lag in the video but its not the case, its only because of my recorder, the game is still runs smoothly (remember, its depends on the computer and the map...) If it helps I have a Lenovo U41-70 notebook (Intel i7 5500U, 8GB DDR3 and GeForce 920M) and I used the "(old) wom2 maps".

    However, I noticed some fps drop with all shadows enabled, and also couldn't test it with many characters (cus the character render consumes so much processing time without this too), so I recommend to disable it by default and if the player decides to use it, he will be able to turn it on.

    Stage 1

    Spoiler

    Some personal hints:

    First let me explain what I meant when I said "chunks". As we all know, each map has a size, like 1x1, 2x2... If a map's size is 2x2 then it has 4 chunks... (I hope its clear now :P )
    Before we start to render the universe we need all the map related things already been loaded and ready for use in the memory. By default the game only loads maximum 9 chunks (the one you are standing on, and when its possible 8 other adjacent ones) but its not enough for us, for example when we are standing on a corner chunk of a 2x3 map we have only 4 chunks loaded so another 2 chunks will be "white" and only levitating trees will be seen. By the way this is the reason when we see hovering trees on the maps: The trees are rendered mapwide by default so even when we don't see them they are there so when the "eye"'s edge is near to a hill or something the tree appears before the terrain.

    You can do this change even if you don't want to "render the galaxy". You may wonder why... The first reason is the useless garbage collector which runs every rendering time and tries to collect unused objects, textures, chunks (even if we don't have any). It could be useful too but in these days its just fcking with the processor... The other reason is quite simple. When you leave a chunk the game instantly starts to load 3 other one (in the worst case) and it brakes the render for some ms which cause an fps drop.

    In this part we will meet with some usual code near to the heart of the area/terrain rendering (no dragons here sry) and we will delete some of them :P Although looks like this part is nearly untouched since the first release of the game and also looks like this part was written by a bit competent person (except the gb collector...) since its not the usual gf like trash code.

    Install (FINALLY)

    GameLib/MapOutdoorUpdate.cpp

    Spoiler

    in bool CMapOutdoor::Update(float fX, float fY, float fZ) replace this:

    
    
    
    
    	if ( bNeedInit ||
    		(m_CurCoordinate.m_sTerrainCoordX/LOAD_SIZE_WIDTH) != (sCoordX/LOAD_SIZE_WIDTH) || 
    		(m_CurCoordinate.m_sTerrainCoordY/LOAD_SIZE_WIDTH) != (sCoordY/LOAD_SIZE_WIDTH) )
    	{
    		if (bNeedInit)
    		{
    			m_PrevCoordinate.m_sTerrainCoordX = sCoordX;
    			m_PrevCoordinate.m_sTerrainCoordY = sCoordY;
    		}
    		else
    		{
    			m_PrevCoordinate.m_sTerrainCoordX = m_CurCoordinate.m_sTerrainCoordX;
    			m_PrevCoordinate.m_sTerrainCoordY = m_CurCoordinate.m_sTerrainCoordY;
    		}
    		
    		m_CurCoordinate.m_sTerrainCoordX = sCoordX;
    		m_CurCoordinate.m_sTerrainCoordY = sCoordY;
    		m_lCurCoordStartX = sCoordX * CTerrainImpl::TERRAIN_XSIZE;
    		m_lCurCoordStartY = sCoordY * CTerrainImpl::TERRAIN_YSIZE;
    
    		WORD wCellCoordX = (ix % CTerrainImpl::TERRAIN_XSIZE) / CTerrainImpl::CELLSCALE;
    		WORD wCellCoordY = (iy % CTerrainImpl::TERRAIN_YSIZE) / CTerrainImpl::CELLSCALE;
    
    		short sReferenceCoordMinX, sReferenceCoordMaxX, sReferenceCoordMinY, sReferenceCoordMaxY;
    		sReferenceCoordMinX = max(m_CurCoordinate.m_sTerrainCoordX - LOAD_SIZE_WIDTH, 0);
    		sReferenceCoordMaxX = min(m_CurCoordinate.m_sTerrainCoordX + LOAD_SIZE_WIDTH, m_sTerrainCountX - 1);
    		sReferenceCoordMinY = max(m_CurCoordinate.m_sTerrainCoordY - LOAD_SIZE_WIDTH, 0);
    		sReferenceCoordMaxY = min(m_CurCoordinate.m_sTerrainCoordY + LOAD_SIZE_WIDTH, m_sTerrainCountY - 1);
    		
    		for (WORD usY = sReferenceCoordMinY; usY <=sReferenceCoordMaxY; ++usY)
    		{
    			for (WORD usX = sReferenceCoordMinX; usX <= sReferenceCoordMaxX; ++usX)
    			{
    				LoadTerrain(usX, usY, wCellCoordX, wCellCoordY);
      				LoadArea(usX, usY, wCellCoordX, wCellCoordY);
    			}
    		}
    
    		AssignTerrainPtr();
    		m_lOldReadX = -1;
    
    		Tracenf("Update::Load spent %d ms\n", ELTimer_GetMSec() - t1);
    	}

    with this:

    
    
    
    
    	if (bNeedInit ||
    		m_CurCoordinate.m_sTerrainCoordX / LOAD_SIZE_WIDTH != sCoordX / LOAD_SIZE_WIDTH ||
    		m_CurCoordinate.m_sTerrainCoordY / LOAD_SIZE_WIDTH != sCoordY / LOAD_SIZE_WIDTH)
    	{
    		if (bNeedInit)
    		{
    			m_PrevCoordinate.m_sTerrainCoordX = sCoordX;
    			m_PrevCoordinate.m_sTerrainCoordY = sCoordY;
    			
    			//during the loading screen load everything
    			for (WORD usY = 0; usY < m_sTerrainCountY; ++usY)
    			{
    				for (WORD usX = 0; usX < m_sTerrainCountX; ++usX)
    				{
    					LoadTerrain(usX, usY);
    					LoadArea(usX, usY);
    				}
    			}
    		}
    		else
    		{
    			m_PrevCoordinate.m_sTerrainCoordX = m_CurCoordinate.m_sTerrainCoordX;
    			m_PrevCoordinate.m_sTerrainCoordY = m_CurCoordinate.m_sTerrainCoordY;
    		}
    
    		m_CurCoordinate.m_sTerrainCoordX = sCoordX;
    		m_CurCoordinate.m_sTerrainCoordY = sCoordY;
    		m_lCurCoordStartX = sCoordX * CTerrainImpl::TERRAIN_XSIZE;
    		m_lCurCoordStartY = sCoordY * CTerrainImpl::TERRAIN_YSIZE;
    
    		AssignTerrainPtr();
    		m_lOldReadX = -1;
    
    		Tracenf("Update::Load spent %d ms\n", ELTimer_GetMSec() - t1);
    	}

    Also delete this from the function:

    
    
    
    
    	__UpdateGarvage();

    In void CMapOutdoor::UpdateTerrain(float fX, float fY) delete this:

    
    
    
    
    	UpdateAreaList(lRealCenterX, lRealCenterY);

    Under this function delete the following functions:

    
    
    
    
    void CMapOutdoor::FPushTerrainToDeleteVector::operator () (CTerrain * pTerrain)
    void CMapOutdoor::FPushAreaToDeleteVector::operator () (CArea * pArea)
    void CMapOutdoor::__ClearGarvage()
    void CMapOutdoor::__UpdateGarvage()
    void CMapOutdoor::UpdateAreaList(long lCenterX, long lCenterY)

     

    GameLib/MapOutdoor.h

    Spoiler

    Delete these things:

    
    
    
    
    		virtual void	__ClearGarvage();
    		virtual void	__UpdateGarvage();
    		virtual void	UpdateAreaList(long lCenterX, long lCenterY);

    And replace these ones (those args are unused, so its just "cleaning"... 😞

    
    
    
    
    		virtual bool	LoadTerrain(WORD wTerrainCoordX, WORD wTerrainCoordY, WORD wCellCoordX, WORD wCellCoordY);
    		virtual bool	LoadArea(WORD wAreaCoordX, WORD wAreaCoordY, WORD wCellCoordX, WORD wCellCoordY);

    With these ones:

    
    
    
    
    		virtual bool	LoadTerrain(WORD wTerrainCoordX, WORD wTerrainCoordY);
    		virtual bool	LoadArea(WORD wAreaCoordX, WORD wAreaCoordY);

    Remove also these things:

    
    
    
    
    		TTerrainPtrVector			m_TerrainDeleteVector;
    		TTerrainPtrVectorIterator	m_TerrainPtrVectorIterator;
    		TTerrainPtrVector			m_TerrainLoadWaitVector;
    		TTerrainPtrVector			m_TerrainLoadRequestVector;
    
    		TAreaPtrVector				m_AreaDeleteVector;
    		TAreaPtrVectorIterator		m_AreaPtrVectorIterator;
    		TAreaPtrVector				m_AreaLoadWaitVector;
    		TAreaPtrVector				m_AreaLoadRequestVector;
    
    		struct FPushToDeleteVector
    		{
    			enum EDeleteDir
    			{
    				DELETE_LEFT,
    				DELETE_RIGHT,
    				DELETE_TOP,
    				DELETE_BOTTOM,
    			};
    
    			EDeleteDir m_eLRDeleteDir;
    			EDeleteDir m_eTBDeleteDir;
    			TOutdoorMapCoordinate m_CurCoordinate;
    
    			FPushToDeleteVector(EDeleteDir eLRDeleteDir, EDeleteDir eTBDeleteDir, TOutdoorMapCoordinate CurCoord)
    			{
    				m_eLRDeleteDir = eLRDeleteDir;
    				m_eTBDeleteDir = eTBDeleteDir;
    				m_CurCoordinate = CurCoord;
    			}
    		};
    		struct FPushTerrainToDeleteVector : public FPushToDeleteVector
    		{
    			TTerrainPtrVector	m_ReturnTerrainVector;
    
    			FPushTerrainToDeleteVector(EDeleteDir eLRDeleteDir, EDeleteDir eTBDeleteDir, TOutdoorMapCoordinate CurCoord)
    				: FPushToDeleteVector(eLRDeleteDir, eTBDeleteDir, CurCoord)
    			{
    				m_ReturnTerrainVector.clear();
    			}
    
    			void operator() (CTerrain * pTerrain);
    		};
    
    		struct FPushAreaToDeleteVector : public FPushToDeleteVector
    		{
    			TAreaPtrVector		m_ReturnAreaVector;
    
    			FPushAreaToDeleteVector(EDeleteDir eLRDeleteDir, EDeleteDir eTBDeleteDir, TOutdoorMapCoordinate CurCoord)
    				: FPushToDeleteVector(eLRDeleteDir, eTBDeleteDir, CurCoord)
    			{
    				m_ReturnAreaVector.clear();
    			}
    
    			void operator() (CArea * pArea);
    		};

     

    MapOutdoorLoad.cpp

    Spoiler

    Replace this:

    
    
    
    
    bool CMapOutdoor::LoadArea(WORD wAreaCoordX, WORD wAreaCoordY, WORD wCellCoordX, WORD wCellCoordY)

    with this:

    
    
    
    
    bool CMapOutdoor::LoadArea(WORD wAreaCoordX, WORD wAreaCoordY)

    And this:

    
    
    
    
    bool CMapOutdoor::LoadTerrain(WORD wTerrainCoordX, WORD wTerrainCoordY, WORD wCellCoordX, WORD wCellCoordY)

    With this:

    
    
    
    
    bool CMapOutdoor::LoadTerrain(WORD wTerrainCoordX, WORD wTerrainCoordY)

     

    MapOutdoor.cpp

    Spoiler

    Delete these ones from void Initialize():

    
    
    
    
    	m_TerrainDeleteVector.clear();
    	m_TerrainLoadRequestVector.clear();
    	m_TerrainLoadWaitVector.clear();
    	
    	m_AreaDeleteVector.clear();
    	m_AreaLoadRequestVector.clear();
    	m_AreaLoadWaitVector.clear();

    From void CMapOutdoor::DestroyArea() delete this:

    
    
    
    
    	m_AreaDeleteVector.clear();

    From void CMapOutdoor::DestroyTerrain() delete this:

    
    
    
    
    	m_TerrainDeleteVector.clear();

     

     

    Stage 2

    Spoiler

    Some personal hints:

    Okay, now here we are, its one of the most complex part of the game (after the statemanager) and here we will see some magical animals (there could be dragons too *o*) like the quad tree and the terrainPatchProxy (yeah the first things we are gonna kick the fck out). These two mentioned things (the quadtree and the proxy) are managing the distance render. When you are just standing and notice that the terrain is just random growing from the ground or random disappears its because these guys (the rendering is depends on the option "far or near"). Since we want to render the whole map we don't need such magic.

    Important: I only modified the HTP rendering, it means that it will work only when the hardware tilling is selected (and ofc the full render is activated).

    GameLib/MapManager.h

    Spoiler

    After this:

    
    
    
    
    		void	ReserveSoftwareTilingEnable(bool isEnable);

    Add this:

    
    
    
    
    		bool	IsFullMapRenderEnable() { return CTerrainPatch::FULL_MAP_RENDER_ENABLE; }
    		void	SetFullMapRender(bool isEnable) { m_isFullMapRenderEnable = isEnable; }

    After this:

    
    
    
    
    		bool m_isSoftwareTilingEnableReserved;

    Add this:

    
    
    
    
    		bool m_isFullMapRenderEnable;

     

    GameLib/MapManager.cpp

    Spoiler

    In CMapManager::CMapManager() add this:

    
    
    
    
    	m_isFullMapRenderEnable = false;

    After this:

    
    
    
    
    	m_isSoftwareTilingEnableReserved=false;

    In void CMapManager::Create() after this:

    
    
    
    
    	CTerrainPatch::SOFTWARE_TRANSFORM_PATCH_ENABLE=m_isSoftwareTilingEnableReserved;

    Add this:

    
    
    
    
    	CTerrainPatch::FULL_MAP_RENDER_ENABLE = m_isFullMapRenderEnable && !IsSoftwareTilingEnable();

     

    GameLib/TerrainPatch.h

    Spoiler

    After this:

    
    
    
    
    	static bool SOFTWARE_TRANSFORM_PATCH_ENABLE;

    Add this:

    
    
    
    
    	static bool FULL_MAP_RENDER_ENABLE;

     

    GameLib/TerrainPatch.cpp

    Spoiler

    After this:

    
    
    
    
    bool CTerrainPatch::SOFTWARE_TRANSFORM_PATCH_ENABLE=TRUE;

    Add this:

    
    
    
    
    bool CTerrainPatch::FULL_MAP_RENDER_ENABLE = false;

     

    GameLib/MapOutdoor.cpp

    Spoiler

    In void CMapOutdoor::CreateTerrainPatchProxyList() replace this:

    
    
    
    
    	m_pTerrainPatchProxyList = new CTerrainPatchProxy[m_wPatchCount * m_wPatchCount];

    With this:

    
    
    
    
    	if (!CTerrainPatch::FULL_MAP_RENDER_ENABLE) // don't need this nasty buddy for full map render
    		m_pTerrainPatchProxyList = new CTerrainPatchProxy[m_wPatchCount * m_wPatchCount];

     

    GameLib/MapOutdoor.h

    Spoiler

    After this:

    
    
    
    
    		void					DrawWater(long patchnum);

    Add this:

    
    
    
    
    		void					DrawFullWater();

    After this:

    
    
    
    
    		void __RenderTerrain_RenderHardwareTransformPatch();

    Add this:

    
    
    
    
    		void RenderWholeMapMB(WORD wPrimitiveCount, D3DPRIMITIVETYPE ePrimitiveType);

     

    GameLib/MapOutdoorLoad.cpp

    Spoiler

    In bool CMapOutdoor::Load(float x, float y, float z) relpace this:

    
    
    
    
    	BuildQuadTree();

    With this:

    
    
    
    
    	if (!CTerrainPatch::FULL_MAP_RENDER_ENABLE) // we don't need this too
    		BuildQuadTree();

     

    GameLib/MapOutdoorRender.cpp

    Spoiler

    In void CMapOutdoor::RenderTerrain() replace this:

    
    
    
    
    	if (!m_pTerrainPatchProxyList)

    With this:

    
    
    
    
    	if (!m_pTerrainPatchProxyList && !CTerrainPatch::FULL_MAP_RENDER_ENABLE) // we don't use proxy and full render together

    Replace this:

    
    
    
    
    	m_PatchVector.clear();
    	
    	__RenderTerrain_RecurseRenderQuadTree(m_pRootNode);
    	
    	// °Ĺ¸®Ľř Á¤·Ä
    	std::sort(m_PatchVector.begin(),m_PatchVector.end());

    With this:

    
    
    
    
    	if (!CTerrainPatch::FULL_MAP_RENDER_ENABLE)
    	{
    		// this part responsible for the distance sorting for the basic render
    		m_PatchVector.clear();
    		__RenderTerrain_RecurseRenderQuadTree(m_pRootNode);
    		std::sort(m_PatchVector.begin(), m_PatchVector.end());
    	}

     

    GameLib/MapOutdoorRenderHTP.cpp

    Spoiler

    Add this to the end of the file:

    
    
    
    
    void CMapOutdoor::RenderWholeMapMB(WORD wPrimitiveCount, D3DPRIMITIVETYPE ePrimitiveType)
    {
    	// now here we might see some dragons, cous I don't know everything's functionality here (and when I know it its because I messed them up before lol...)
    	// its mostly a mixed up code from the original ones
    	DWORD dwFogColor;
    	bool bEnableFog = false;
    	if (mc_pEnvironmentData) // check if the fog is enabled and its color
    	{
    		dwFogColor = mc_pEnvironmentData->FogColor;
    		bEnableFog = mc_pEnvironmentData->bFogEnable;
    	}
    
    	for (auto it = m_TerrainVector.begin(); it != m_TerrainVector.end(); ++it) // lets start render the whole map (all chunks)
    	{
    		CTerrain * pTerrain = (*it);
    
    		WORD wCoordX, wCoordY;
    		pTerrain->GetCoordinate(&wCoordX, &wCoordY);
    
    		TTerrainSplatPatch & rTerrainSplatPatch = pTerrain->GetTerrainSplatPatch();
    		for (BYTE y = 0; y < CTerrainImpl::PATCH_YCOUNT; ++y)
    		{
    			for (BYTE x = 0; x < CTerrainImpl::PATCH_XCOUNT; ++x)
    			{
    				// each chunk contains 64 patches, and its rendering order is important, otherwise we might would see some abstract textures :P
    				CTerrainPatch* pTerrainPath = pTerrain->GetTerrainPatchPtr(x, y);
    				if (!pTerrainPath)
    					continue;
    
    				// OKAY we gonna do SOMETHING with alpha texures and tiling here, I'm not a graphical processing expert...
    				D3DXMATRIX matTexTransform, matSplatAlphaTexTransform, matSplatColorTexTransform;
    				m_matWorldForCommonUse._41 = -(float)(wCoordX * CTerrainImpl::TERRAIN_XSIZE);
    				m_matWorldForCommonUse._42 = (float)(wCoordY * CTerrainImpl::TERRAIN_YSIZE);
    				D3DXMatrixMultiply(&matTexTransform, &m_matViewInverse, &m_matWorldForCommonUse);
    				D3DXMatrixMultiply(&matSplatAlphaTexTransform, &matTexTransform, &m_matSplatAlpha);
    				STATEMANAGER.SetTransform(D3DTS_TEXTURE1, &matSplatAlphaTexTransform);
    
    				D3DXMATRIX matTiling;
    				D3DXMatrixScaling(&matTiling, 1.0f / 640.0f, -1.0f / 640.0f, 0.0f);
    				matTiling._41 = 0.0f;
    				matTiling._42 = 0.0f;
    
    				D3DXMatrixMultiply(&matSplatColorTexTransform, &m_matViewInverse, &matTiling);
    				STATEMANAGER.SetTransform(D3DTS_TEXTURE0, &matSplatColorTexTransform);
    
    				CGraphicVertexBuffer* pkVB = pTerrainPath->HardwareTransformPatch_GetVertexBufferPtr();
    				if (!pkVB)
    					return;
    
    				STATEMANAGER.SetStreamSource(0, pkVB->GetD3DVertexBuffer(), m_iPatchTerrainVertexSize);
    				STATEMANAGER.SetRenderState(D3DRS_LIGHTING, FALSE);
    
    				bool isFirst = true;
    				for (DWORD j = 1; j < pTerrain->GetNumTextures(); ++j)
    				{
    					// okay lets start rendering the textures here (still don't fully get this part...)
    					TTerainSplat & rSplat = rTerrainSplatPatch.Splats[j];
    
    					if (!rSplat.Active)
    						continue;
    
    					if (rTerrainSplatPatch.PatchTileCount[y * CTerrainImpl::PATCH_YCOUNT + x][j] == 0)
    						continue;
    
    					const TTerrainTexture & rTexture = m_TextureSet.GetTexture(j);
    					D3DXMatrixMultiply(&matSplatColorTexTransform, &m_matViewInverse, &rTexture.m_matTransform);
    					STATEMANAGER.SetTransform(D3DTS_TEXTURE0, &matSplatColorTexTransform);
    					if (isFirst)
    					{
    						STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
    						STATEMANAGER.SetTexture(0, rTexture.pd3dTexture);
    						STATEMANAGER.SetTexture(1, rSplat.pd3dTexture);
    						STATEMANAGER.DrawIndexedPrimitive(ePrimitiveType, 0, m_iPatchTerrainVertexCount, 0, wPrimitiveCount);
    						STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
    						isFirst = false;
    					}
    					else
    					{
    						STATEMANAGER.SetTexture(0, rTexture.pd3dTexture);
    						STATEMANAGER.SetTexture(1, rSplat.pd3dTexture);
    						STATEMANAGER.DrawIndexedPrimitive(ePrimitiveType, 0, m_iPatchTerrainVertexCount, 0, wPrimitiveCount);
    					}
    
    				}
    
    				if (m_bDrawShadow) // lets render the shadows (its still some sort of graphic processing magic)
    				{
    					STATEMANAGER.SetRenderState(D3DRS_LIGHTING, TRUE);
    					STATEMANAGER.SetRenderState(D3DRS_FOGCOLOR, 0xFFFFFFFF);
    					STATEMANAGER.SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
    					STATEMANAGER.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);
    
    					D3DXMATRIX matShadowTexTransform;
    					D3DXMatrixMultiply(&matShadowTexTransform, &matTexTransform, &m_matStaticShadow);
    
    					STATEMANAGER.SetTransform(D3DTS_TEXTURE0, &matShadowTexTransform);
    					STATEMANAGER.SetTexture(0, pTerrain->GetShadowTexture());
    
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
    
    					if (m_bDrawChrShadow)
    					{
    						STATEMANAGER.SetTransform(D3DTS_TEXTURE1, &m_matDynamicShadow);
    						STATEMANAGER.SetTexture(1, m_lpCharacterShadowMapTexture);
    						STATEMANAGER.SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    						STATEMANAGER.SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
    						STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
    						STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
    						STATEMANAGER.SetTextureStageState(1, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
    						STATEMANAGER.SetTextureStageState(1, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
    					}
    					else
    					{
    						STATEMANAGER.SetTexture(1, NULL);
    					}
    
    					ms_faceCount += wPrimitiveCount;
    					STATEMANAGER.DrawIndexedPrimitive(ePrimitiveType, 0, m_iPatchTerrainVertexCount, 0, wPrimitiveCount);
    					++m_iRenderedSplatNum;
    
    					if (m_bDrawChrShadow)
    					{
    						STATEMANAGER.SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
    						STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
    						STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    						STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
    					}
    
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
    					STATEMANAGER.SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
    
    
    					STATEMANAGER.SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
    					STATEMANAGER.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
    					if (bEnableFog)
    						STATEMANAGER.SetRenderState(D3DRS_FOGCOLOR, dwFogColor);
    
    					STATEMANAGER.SetRenderState(D3DRS_LIGHTING, FALSE);
    				}
    			}
    		}
    	}
    }

    In void CMapOutdoor::__RenderTerrain_RenderHardwareTransformPatch() after this:

    
    
    
    
    	float fFogNearDistance;

    Add this:

    
    
    
    
    	bool bEnableFog; //there is an option in the map's config to disable config but LOL its not implemented properly...

    After this:

    
    
    
    
    		fFogFarDistance=mc_pEnvironmentData->GetFogFarDistance();

    Add this:

    
    
    
    
    		bEnableFog = mc_pEnvironmentData->bFogEnable;

    Replace this:

    
    
    
    
    	{
    		dwFogColor=0xffffffff;
    		fFogNearDistance=5000.0f;
    		fFogFarDistance=10000.0f;
    	}

    With this:

    
    
    
    
    		bEnableFog = false;

    Replace this:

    
    
    
    
    	STATEMANAGER.SaveRenderState(D3DRS_TEXTUREFACTOR, dwFogColor);

    With this:

    
    
    
    
    	if (bEnableFog) // we don't need fog render when no fog activated
    		STATEMANAGER.SaveRenderState(D3DRS_TEXTUREFACTOR, dwFogColor);

    Replace this:

    
    
    
    
    	DWORD dwFogEnable = STATEMANAGER.GetRenderState(D3DRS_FOGENABLE);

    With this:

    
    
    
    
    	DWORD dwFogEnable = STATEMANAGER.GetRenderState(D3DRS_FOGENABLE);
    	if (!bEnableFog)
    		dwFogEnable = 0;

    Add this:

    
    
    
    
    	if (!CTerrainPatch::FULL_MAP_RENDER_ENABLE) // when full load is disabled this part renders the near things, without any fog

    Before this:

    
    
    
    
    	for( ; it != near_it; ++it)

    Before this:

    
    
    
    
    	if (m_iRenderedSplatNum < m_iSplatLimit) //there are two of them this is why I inserted the next few lines too
    	{
    		for(it = near_it; it != far_it; ++it)
    		{

    Add this:

    
    
    
    
    	if (CTerrainPatch::FULL_MAP_RENDER_ENABLE)
    		RenderWholeMapMB(wPrimitiveCount, ePrimitiveType);

    Replace this (there are two of them in the code, replace both):

    
    
    
    
    	if (m_iRenderedSplatNum < m_iSplatLimit)

    With this:

    
    
    
    
    	if (!CTerrainPatch::FULL_MAP_RENDER_ENABLE && m_iRenderedSplatNum < m_iSplatLimit)

     

    GameLib/MapOutdoorUpdate.cpp

    Spoiler

    In void CMapOutdoor::UpdateTerrain(float fX, float fY) before this:

    
    
    
    
    		ConvertTerrainToTnL(lRealCenterX, lRealCenterY);

    Add this:

    
    
    
    
    		if (!CTerrainPatch::FULL_MAP_RENDER_ENABLE) // the ConvertTerrainToTnl fills the quadtree, but we don't need this when full render active

     

    Okay, now the terrain should be fully rendered if you activate it by setting m_isFullMapRenderEnable = true in the CMapManager::CMapManager() (and remember: you must set the tilling mode to GPU), but yet the water would be missing, so lets fix it.

    GameLib/MapOutdoorWater.cpp

    Spoiler

    Add this to the end of the file:

    
    
    
    
    void CMapOutdoor::DrawFullWater()
    {
    	CTerrain* terra;
    	CTerrainPatch* pTerrainPath;
    	CGraphicVertexBuffer* pkVB;
    	UINT uPriCount;
    	for (auto it = m_TerrainVector.begin(); it != m_TerrainVector.end(); ++it)
    	{
    		terra = *it;
    		for (BYTE y = 0; y < CTerrainImpl::PATCH_YCOUNT; ++y)
    		{
    			for (BYTE x = 0; x < CTerrainImpl::PATCH_XCOUNT; ++x)
    			{
    				pTerrainPath = terra->GetTerrainPatchPtr(x, y);
    				if (!pTerrainPath || !pTerrainPath->IsWaterExist())
    					continue;
    
    				pkVB = pTerrainPath->GetWaterVertexBufferPointer();
    				if (!pkVB || !pkVB->GetD3DVertexBuffer())
    					continue;
    
    				uPriCount = pTerrainPath->GetWaterFaceCount();
    				if (!uPriCount)
    					return;
    
    				STATEMANAGER.SetStreamSource(0, pkVB->GetD3DVertexBuffer(), sizeof(SWaterVertex));
    				STATEMANAGER.DrawPrimitive(D3DPT_TRIANGLELIST, 0, uPriCount);
    			}
    		}
    	}
    }

    In void CMapOutdoor::RenderWater() replace this:

    
    
    
    
    	if (m_PatchVector.empty())

    With this:

    
    
    
    
    	if (m_PatchVector.empty() && !CTerrainPatch::FULL_MAP_RENDER_ENABLE) // the patch vector will always be empty when using full render

    Replace this:

    
    
    
    
    	std::vector<std::pair<float, long> >::iterator i;
    
    	for (i = m_PatchVector.begin(); i != m_PatchVector.end(); ++i)
    	{
    		if (i->first < fFogDistance)
    			DrawWater(i->second);
    	}

    With this:

    
    
    
    
    	if (!CTerrainPatch::FULL_MAP_RENDER_ENABLE)
    	{
    		std::vector<std::pair<float, long> >::iterator i;
    
    		for (i = m_PatchVector.begin(); i != m_PatchVector.end(); ++i)
    		{
    			if (i->first < fFogDistance)
    				DrawWater(i->second);
    		}
    	}
    	else
    		DrawFullWater();

    Then this:

    
    
    
    
    	for (i = m_PatchVector.begin(); i != m_PatchVector.end(); ++i)
    	{
    		if (i->first >= fFogDistance)
    			DrawWater(i->second);
    	}

    With this:

    
    
    
    
    	if (!CTerrainPatch::FULL_MAP_RENDER_ENABLE) // this part disables the water behind fog when full render disabled (but we need that water when enabled!)
    	{
    		std::vector<std::pair<float, long> >::iterator i;
    
    		for (i = m_PatchVector.begin(); i != m_PatchVector.end(); ++i)
    		{
    			if (i->first >= fFogDistance)
    				DrawWater(i->second);
    		}
    	}

     

    Now the render should be done, but you have increase the fog level in the constinfo, because it controls the "eye", which cuts the terrain and the other things by that white thing... For me example:

    ReIlJUF.png

    Stage 3

    Spoiler

    Not right now :P  So yeah as you may noticed on the video, the building rendering is the same as the terrain rendering, so only renders the building on the current and on 8 others chunks. If you have fog enabled you probably won't notice that they are not there :D Hopefully I will do it sometimes soon, but no promises...

    Stage 4

    Spoiler

    Yeeeah so atm if you want to enable it you have to set the m_isFullMapRenderEnable to true, its enable by default (you can set it in CMapManager::CMapManager() in the MapManager.cpp, but if you don't want to wait me to do it you can easily code it, just have to call SetFullMapRender when you load the config file (and add a new variable to the config ofc).

    So yeah this is it so far, if you have problem with the code (not compiling, etc) is probably because you did something wrong, I made these steps too on a full untouched client source so it should work for you too... (So pls don't ask me to help with this :D ) However, if something is not clear in the tutorial you can ask me for pictures or clarification. I didn't test it in dungeons (indoor maps) so there could be problems...

    Also if you made this and you have some nice maps or you find some great spots for some ingame pictures or videos, feel free to post them here :)

    • Metin2 Dev 4
    • Sad 1
    • Think 1
    • Confused 2
    • Good 1
    • muscle 1
    • Love 1
    • Love 26
  13. I think its hard to compare, the build time depends on the computer: Mostly processor and ssd/hdd write time. Also if you have large libs or large object files that the linker have to put into the binary its even takes more time, for example if you have static python lib or cythonized root it takes so much time, for me sometimes ~10min

    Btw the difference between Distribute and Release is that there are some #ifdefs that are using distribute's macro, and if its compiled in release some part of the code will work like if it would be in debug (for example the debug strings in the client) because of the mentioned ifdefs check... And yeah also can be some configuration difference too but its vary too...

    • Love 1
  14. The view distance only modifies the range for the broadcast packet (I mean with larger viewdistance you will see more players). For the terrain you will need something like this:

    Spoiler

     

    I made this about a year ago and I wanted to release it a few times before, maybe I will do it sometimes soon, but the best solution for this if it would be disabled by default, and players would have the option to turn it on, cus the "engine" itself can't render much objects, so on low spec pcs it would be an instakill. However, while I was testing it the only mentionable difference I found was with the shadows, if I disabled it the performance was near the same, but I never had the chance to test it on a live server with large amount of players/shops.

    • Love 2
  15. 1 hour ago, Raylee said:

    Are you sure?
    We live in the Year 2018 :D
    I don't think that there exists many old PC's.

    Maybe I'm wrong..


    Best regards
    Raylee

    In Hungary players sometimes complaining about that they cant start their clients on WinXP 417679821447888898.png?v=1

    • Confused 1
  16. Also note that on freebsd 64 bit with gcc the long's length is 64 bit, which is quite irritating when you want to send packets from the client to the server containing long (because they are 32 bits long) so therefore you have to replace the longs on serverside to int or replace the longs on clientside to long longs to match the packets size (only in the packets ofc).

    • Love 1
×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.