Jump to content

Trial

Active Member
  • Posts

    70
  • Joined

  • Last visited

  • Days Won

    3
  • Feedback

    0%

Trial last won the day on November 4 2014

Trial had the most liked content!

About Trial

Informations

  • Gender
    Male

Recent Profile Visitors

5049 profile views

Trial's Achievements

Collaborator

Collaborator (7/16)

  • Dedicated
  • Reacting Well
  • Very Popular Rare
  • First Post
  • Collaborator Rare

Recent Badges

326

Reputation

  1. he's referring to "x32" which is actually x86 architecture thx for sharing, btw speedtree lib already shared here:
  2. Nice, I always thought of adding this to metin!
  3. you made a mistake in SpeedTreeForest.cpp, you must just add // For main tree instance explicitely make CSpeedTreeRT object // for instances it's "instanced" from main tree in "MakeInstance" pTree->MakeSpeedTree(); below pTree = new CSpeedTreeWrapper; result should be: BOOL CSpeedTreeForest::GetMainTree(DWORD dwCRC, CSpeedTreeWrapper ** ppMainTree, const char * c_pszFileName) { TTreeMap::iterator itor = m_pMainTreeMap.find(dwCRC); CSpeedTreeWrapper * pTree; if (itor != m_pMainTreeMap.end()) pTree = itor->second; else { CMappedFile file; LPCVOID c_pvData; if (!CEterPackManager::Instance().Get(file, c_pszFileName, &c_pvData)) return FALSE; pTree = new CSpeedTreeWrapper; // For main tree instance explicitely make CSpeedTreeRT object // for instances it's "instanced" from main tree in "MakeInstance" pTree->MakeSpeedTree(); if (!pTree->LoadTree(c_pszFileName, (const BYTE *) c_pvData, file.Size())) { delete pTree; return FALSE; } m_pMainTreeMap.insert(std::map<DWORD, CSpeedTreeWrapper *>::value_type(dwCRC, pTree)); file.Destroy(); } *ppMainTree = pTree; return TRUE; }
  4. No, don't, don't heap-allocate useless resource juste to delete it afterwards
  5. Can't see how this could be related, could you provide me a git diff of the changes you applied?
  6. This is a trick I implemented on a player's suggestion, the idea is to block the "regen" (mobs respawning) in some areas until a specific monster is killed. So if you want to prevent players from going AFK & macro in some areas you put a "regen blocking mob" there, preferably a range-based attacking one (magic / archer) so that this area's monsters will not be respawned until this monster is killed. These monsters will be spawned via the regen system and you will be able to control the area covered by individual monster using it's spawn position and delta x & delta y coordinates. regen.h somewhere in this struct: typedef struct regen add: bool is_anti_macro; and add it in the initializers list: regen() : prev(NULL), next(NULL), ... is_anti_macro(false), // <-- this event(NULL), id(0) {} add: extern bool is_regen_in_anti_macro_position(LPREGEN tested_regen); somewhere near: extern void regen_reset(int x, int y); regen.cpp below: LPREGEN regen_list = NULL; add: LPREGEN regen_anti_macro_list = NULL; in this function: static bool read_line(FILE* fp, LPREGEN regen) edit this case-if block case MODE_TYPE: if (szTmp[0] == 'm') to this: ... case MODE_TYPE: if (szTmp[0] == 'm') { regen->type = REGEN_TYPE_MOB; if (szTmp[1] == 'a' && szTmp[2] == 'm') regen->is_anti_macro = true; } ... add this function somewhere in the file: bool is_regen_in_anti_macro_position(LPREGEN tested_regen) { LPREGEN regen; for (regen = regen_anti_macro_list; regen; regen = regen->next) { // if "tested_regen" position is inside "regen" position if (regen->count) if (tested_regen->sx >= regen->sx && tested_regen->ex <= regen->ex && tested_regen->sy >= regen->sy && tested_regen->ey <= regen->ey) { return true; } } return false; } in this function's definition: static void regen_spawn(LPREGEN regen, bool bOnce) edit the return type to bool and add the third input parameter: static bool regen_spawn(LPREGEN regen, bool bOnce, bool bInitial = false) change: if (!num) return; to: if (!num) return true; and add this just below: if (!bInitial && !regen->is_anti_macro) { if (is_regen_in_anti_macro_position(regen)) return false; } and finally add this return at the end: return true; the whole definition should look like this: static bool regen_spawn(LPREGEN regen, bool bOnce, bool bInitial = false) { DWORD num; DWORD i; num = (regen->max_count - regen->count); if (!num) return true; if (!bInitial && !regen->is_anti_macro) { if (is_regen_in_anti_macro_position(regen)) return false; } for (i = 0; i < num; ++i) { LPCHARACTER ch = NULL; if (regen->type == REGEN_TYPE_ANYWHERE) { ch = CHARACTER_MANAGER::instance().SpawnMobRandomPosition(regen->vnum, regen->lMapIndex); if (ch) ++regen->count; } else if (regen->sx == regen->ex && regen->sy == regen->ey) { ch = CHARACTER_MANAGER::instance().SpawnMob(regen->vnum, regen->lMapIndex, regen->sx, regen->sy, regen->z_section, false, regen->direction == 0 ? number(0, 7) * 45 : (regen->direction - 1) * 45); if (ch) ++regen->count; } else { if (regen->type == REGEN_TYPE_MOB) { ch = CHARACTER_MANAGER::Instance().SpawnMobRange(regen->vnum, regen->lMapIndex, regen->sx, regen->sy, regen->ex, regen->ey, true, regen->is_aggressive, regen->is_aggressive ); if (ch) ++regen->count; } else if (regen->type == REGEN_TYPE_GROUP) { if (CHARACTER_MANAGER::Instance().SpawnGroup(regen->vnum, regen->lMapIndex, regen->sx, regen->sy, regen->ex, regen->ey, bOnce ? NULL : regen, regen->is_aggressive)) ++regen->count; } else if (regen->type == REGEN_TYPE_GROUP_GROUP) { if (CHARACTER_MANAGER::Instance().SpawnGroupGroup(regen->vnum, regen->lMapIndex, regen->sx, regen->sy, regen->ex, regen->ey, bOnce ? NULL : regen, regen->is_aggressive)) ++regen->count; } } if (ch && !bOnce) ch->SetRegen(regen); } return true; } in this function: EVENTFUNC(regen_event) edit this: regen_spawn(regen, false); return PASSES_PER_SEC(regen->time); to: if (!regen_spawn(regen, false)) { // if the regen failed (probably because there is a regen blocking mob alive in the regen area) // let's retry faster, NB: YOU CAN reduce this value further is you like return PASSES_PER_SEC(regen->time / 2); } return PASSES_PER_SEC(regen->time); the whole definition should look like this: EVENTFUNC(regen_event) { regen_event_info* info = dynamic_cast<regen_event_info*>( event->info ); if ( info == NULL ) { sys_err( "regen_event> <Factor> Null pointer" ); return 0; } LPREGEN regen = info->regen; if (regen->time == 0) regen->event = NULL; if (!regen_spawn(regen, false)) { // if the regen failed (probably because there is a regen blocking mob alive in the regen area) // let's retry faster, NB: YOU CAN reduce this value further is you like return PASSES_PER_SEC(regen->time / 2); } return PASSES_PER_SEC(regen->time); } in: void regen_free(void) add this add the end: regen_anti_macro_list = NULL; Now you can add the regen blocking monsters in your regen files as you would do for normal monsters & groups: example below with the mob vnum 2132, the regen definition remains identical to normal monster one: Bonus: if you want your regen blocking monsters to have custom hard-coded attacking range you could edit this function (in char.cpp): WORD CHARACTER::GetMobAttackRange() const to something like: WORD CHARACTER::GetMobAttackRange() const { switch (GetMobBattleType()) { case BATTLE_TYPE_RANGE: case BATTLE_TYPE_MAGIC: if (m_pkRegen && m_pkRegen->is_anti_macro) return 2400; // put your custom value here return m_pkMobData->m_table.wAttackRange + GetPoint(POINT_BOW_DISTANCE); default: return m_pkMobData->m_table.wAttackRange; } } This is a simple example of implementing this idea, you can go further if you wish
      • 5
      • Metin2 Dev
      • Love
      • muscle
  7. There is a memory leak in the SpeedTree library implementation used in metin2 client, nothing too serious about memory usage but it also affects CPU usage. There is a leaked "CSpeedTreeRT" instance for each tree instance created at runtime, this can escalate quickly after a few hours of metinstone farming. Every CSpeedTreeRT instance is "updated" (including the leaked ones) when "CSpeedTreeRT::SetTime" is called (once per frame obviously) resulting extra memory and CPU usage wasted. The leak occurs when "CSpeedTreeWrapper::MakeInstance" is called (when a tree instance is created from a "main tree") when a "CSpeedTreeWrapper" instance is created we have a "CSpeedTreeRT" instance (m_pSpeedTree) this ptr is then reassigned without freeing (pInstance->m_pSpeedTree = m_pSpeedTree->MakeInstance();) You can fix it in many ways, go the way you like, here is one (git patch download below) Download Spoiler diff --git a/SpeedTreeLib/SpeedTreeForest.cpp b/SpeedTreeLib/SpeedTreeForest.cpp index b7342de..4e4ba5e 100644 --- a/SpeedTreeLib/SpeedTreeForest.cpp +++ b/SpeedTreeLib/SpeedTreeForest.cpp @@ -84,6 +84,10 @@ BOOL CSpeedTreeForest::GetMainTree(DWORD dwCRC, CSpeedTreeWrapper ** ppMainTree, pTree = new CSpeedTreeWrapper; + // For main tree instance explicitely make CSpeedTreeRT object + // for instances it's "instanced" from main tree in "MakeInstance" + pTree->MakeSpeedTree(); + if (!pTree->LoadTree(c_pszFileName, (const BYTE *) c_pvData, file.Size())) { delete pTree; diff --git a/SpeedTreeLib/SpeedTreeWrapper.cpp b/SpeedTreeLib/SpeedTreeWrapper.cpp index 87eb836..3ca6016 100644 --- a/SpeedTreeLib/SpeedTreeWrapper.cpp +++ b/SpeedTreeLib/SpeedTreeWrapper.cpp @@ -57,7 +57,7 @@ bool CSpeedTreeWrapper::ms_bSelfShadowOn = true; /////////////////////////////////////////////////////////////////////// // CSpeedTreeWrapper::CSpeedTreeWrapper CSpeedTreeWrapper::CSpeedTreeWrapper() : -m_pSpeedTree(new CSpeedTreeRT), +m_pSpeedTree(nullptr), m_bIsInstance(false), m_pInstanceOf(NULL), m_pGeometryCache(NULL), @@ -73,12 +73,28 @@ m_pLeavesUpdatedByCpu(NULL), m_unBranchVertexCount(0), m_unFrondVertexCount(0), m_pTextureInfo(NULL) +{ + Initialize(); +} + +void CSpeedTreeWrapper::Initialize() { // set initial position m_afPos[0] = m_afPos[1] = m_afPos[2] = 0.0f; - - m_pSpeedTree->SetWindStrength(1.0f); - m_pSpeedTree->SetLocalMatrices(0, 4); + + if (m_pSpeedTree) + { + m_pSpeedTree->SetWindStrength(1.0f); + m_pSpeedTree->SetLocalMatrices(0, 4); + } +} + +void CSpeedTreeWrapper::MakeSpeedTree() +{ + assert(m_pSpeedTree == nullptr); + + m_pSpeedTree = new CSpeedTreeRT; + Initialize(); } void CSpeedTreeWrapper::SetVertexShaders(DWORD dwBranchVertexShader, DWORD dwLeafVertexShader) diff --git a/SpeedTreeLib/SpeedTreeWrapper.h b/SpeedTreeLib/SpeedTreeWrapper.h index ea9e9ce..86ffa89 100644 --- a/SpeedTreeLib/SpeedTreeWrapper.h +++ b/SpeedTreeLib/SpeedTreeWrapper.h @@ -97,6 +97,8 @@ public: public: CSpeedTreeWrapper(); virtual ~CSpeedTreeWrapper(); + + void Initialize(); const float * GetPosition(); static void SetVertexShaders(DWORD dwBranchVertexShader, DWORD dwLeafVertexShader); @@ -128,6 +130,7 @@ public: CSpeedTreeWrapper * InstanceOf(void) const { return m_pInstanceOf; } CSpeedTreeWrapper * MakeInstance(); void DeleteInstance(CSpeedTreeWrapper * pInstance); + void MakeSpeedTree(); CSpeedTreeRT * GetSpeedTree(void) const { return m_pSpeedTree; } // lighting in SpeedTreeWrapper.h: add these somewhere in CSpeedTreeWrapper class declaration ("MakeSpeedTree" must be public, "Initialize" can be private): void Initialize(); void MakeSpeedTree(); in SpeedTreeWrapper.cpp: replace CSpeedTreeWrapper::CSpeedTreeWrapper() definition to (notice "m_pSpeedTree" initialization to nullptr here): CSpeedTreeWrapper::CSpeedTreeWrapper() : m_pSpeedTree(nullptr), m_bIsInstance(false), m_pInstanceOf(NULL), m_pGeometryCache(NULL), m_usNumLeafLods(0), m_pBranchIndexCounts(NULL), m_pBranchIndexBuffer(NULL), m_pBranchVertexBuffer(NULL), m_pFrondIndexCounts(NULL), m_pFrondIndexBuffer(NULL), m_pFrondVertexBuffer(NULL), m_pLeafVertexBuffer(NULL), m_pLeavesUpdatedByCpu(NULL), m_unBranchVertexCount(0), m_unFrondVertexCount(0), m_pTextureInfo(NULL) { Initialize(); } add: void CSpeedTreeWrapper::Initialize() { // set initial position m_afPos[0] = m_afPos[1] = m_afPos[2] = 0.0f; if (m_pSpeedTree) { m_pSpeedTree->SetWindStrength(1.0f); m_pSpeedTree->SetLocalMatrices(0, 4); } } void CSpeedTreeWrapper::MakeSpeedTree() { assert(m_pSpeedTree == nullptr); m_pSpeedTree = new CSpeedTreeRT; Initialize(); } in SpeedTreeForest.cpp in BOOL CSpeedTreeForest::GetMainTree(DWORD dwCRC, CSpeedTreeWrapper ** ppMainTree, const char * c_pszFileName) below: pTree = new CSpeedTreeWrapper; add: // For main tree instance explicitely make CSpeedTreeRT object // for instances it's "instanced" from main tree in "MakeInstance" pTree->MakeSpeedTree(); Done
  8. if (Cell == DestCell) return false;
  9. i don't think it's related, the bug is present on any map/location, it only depends on the call to CHARACTER::CalculateMoveDuration which sets the m_dwMoveDuration to 0 if CHARACTER::m_posStart equals CHARACTER::m_posDest it's easy to trigger : login, click somewhere to move (do not release mouse button) + CTRL+G to mount -> undefined behavior, it might be "fine" if the result of (float)dwElapsedTime / (float)m_dwMoveDuration is positive (inf) but if the result is negative (-nan / -inf) it results in -INTMAX coordinates. Anyway it's undefined behavior in all cases ?
  10. I still recommend you to apply this fix, I have folders configured as they should be but the problem persisted because of the undefined behavior I quoted above. SYSERR: Dec 26 17:40:03 :: CHARACTER::Sync: cannot find tree at -2147483648 -2147483648 (name: sync) Edit: Not same bug I think, the one caused by this undefined behavior is not directly related to motion files.
  11. I'm digging up this old topic because I'm doing some tests on new server files and I just encountered this problem. This solutions does work but it doesn't fix the issue at it's origin/source. The origin lies in "CHARACTER::StateMove", here: float fRate = (float)dwElapsedTime / (float)m_dwMoveDuration; it sometimes happens that "m_dwMoveDuration" equals 0 thus resulting in undefined behavior (see "CHARACTER::CalculateMoveDuration" for more info) in "CHARACTER::StateMove" replace this DWORD dwElapsedTime = get_dword_time() - m_dwMoveStartTime; float fRate = (float)dwElapsedTime / (float)m_dwMoveDuration; if (fRate > 1.0f) fRate = 1.0f; int x = (int)((float)(m_posDest.x - m_posStart.x) * fRate + m_posStart.x); int y = (int)((float)(m_posDest.y - m_posStart.y) * fRate + m_posStart.y); with this const DWORD dwElapsedTime = get_dword_time() - m_dwMoveStartTime; int x; int y; bool bMovementFinished = false; //indicates if character moved and has reached destination if (!dwElapsedTime || !m_dwMoveDuration) { x = GetX(); y = GetY(); } else { float fRate = (float)dwElapsedTime / (float)m_dwMoveDuration; if (fRate >= 1.0f) { bMovementFinished = true; fRate = 1.0f; } x = (int)((float)(m_posDest.x - m_posStart.x) * fRate + m_posStart.x); y = (int)((float)(m_posDest.y - m_posStart.y) * fRate + m_posStart.y); } still in same function replace this if (1.0f == fRate) with this if (bMovementFinished)
  12. This happens because the "Anisotropic Texture Filtering" is only applied when instancing "CStateManager" (CStateManager::SetDevice) it must be applied again after losing/resetting the D3D device. This could be done by moving this code below from "CStateManager::SetDevice" to "CStateManager::SetDefaultState" D3DCAPS8 d3dCaps; m_lpD3DDev->GetDeviceCaps(&d3dCaps); if (d3dCaps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFANISOTROPIC) m_dwBestMagFilter = D3DTEXF_ANISOTROPIC; else m_dwBestMagFilter = D3DTEXF_LINEAR; if (d3dCaps.TextureFilterCaps & D3DPTFILTERCAPS_MINFANISOTROPIC) m_dwBestMinFilter = D3DTEXF_ANISOTROPIC; else m_dwBestMinFilter = D3DTEXF_LINEAR; DWORD dwMax = d3dCaps.MaxAnisotropy; dwMax = dwMax < 4 ? dwMax : 4; for (int i = 0; i < 8; ++i) m_lpD3DDev->SetTextureStageState(i, D3DTSS_MAXANISOTROPY, dwMax); so that "CStateManager::SetDevice" looks like this void CStateManager::SetDevice(LPDIRECT3DDEVICE8 lpDevice) { StateManager_Assert(lpDevice); lpDevice->AddRef(); if (m_lpD3DDev) { m_lpD3DDev->Release(); m_lpD3DDev = NULL; } m_lpD3DDev = lpDevice; SetDefaultState(); } (I know the "m_dwBestMagFilter" and "m_dwBestMinFilter" setting part does not need to be called every time device is reset, you are free to edit the code at your will, I kept things simple for the post) alternatively these lines could be moved to a new method that will be called in "CStateManager::SetDevice" and wherever there is D3D device reset. Although I recommend first solution.
  13. Did this a while ago after noticing freak memory usage, I recommend you add these checks as well for more optimization: else if (m_me->IsType(ENTITY_ITEM) && ent->IsType(ENTITY_ITEM)) { //NOTE: no item to item insert return; } else if (m_me->IsType(ENTITY_ITEM) && ent->IsType(ENTITY_CHARACTER) && !ent->GetDesc()) { //NOTE: no item to NPC insert return; } else if (m_me->IsType(ENTITY_CHARACTER) && !m_me->GetDesc() && ent->IsType(ENTITY_ITEM)) { //NOTE: no NPC to item insert return; }
  14. Since I can't edit the original post (?) here are some explanations for those who are interested in the details: DB Cache should never be flushed manually, better let the normal cache handling process do it's work.
  15. What the.. ? Anyway, glad it helped!
×
×
  • 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.