Jump to content

Syreldar

Honorable Member
  • Posts

    1406
  • Joined

  • Last visited

  • Days Won

    41
  • Feedback

    100%

Syreldar last won the day on October 27

Syreldar had the most liked content!

About Syreldar

  • Birthday 12/22/1997

Informations

  • Gender
    Male
  • Country
    Italy
  • Nationality
    Italian

Recent Profile Visitors

21562 profile views

Syreldar's Achievements

Veteran

Veteran (13/16)

  • Well Followed Rare
  • Reacting Well
  • Dedicated
  • Very Popular Rare
  • One Year In

Recent Badges

2.8k

Reputation

  1. Hi all, You've probably seen this one: someone shoots a mob, or casts chain lightning, and the projectile flies off to an empty spot or towards the corner of the map. Same thing with your own shots. I tracked it down in the client, and it comes from two places. 1. `CFlyTarget::Clear()` never unregisters Every actor keeps a set of the `CFlyTargets` aimed at it. When the actor dies, it calls `NotifyTargetClear()` on each of them, which turns them into fixed-position shots. `Clear()` resets its pointer but doesn't remove itself from that set, so the old target keeps a pointer to it. Fly targets get reused. Actors aim at a new mob with the same object, and `CFlyingInstance` is pooled and calls `m_FlyTarget.Clear()` on destroy. When the old mob dies later, it converts the reused fly target, which is now aimed at another mob, into a position shot. The position comes from `m_v3FlyTargetPosition`. That's a cache that only updates when something reads it, and it starts at `(0,0,0)` when the fly target is built from an actor pointer. So the shot goes to a stale spot or to the map origin. Multi-target skills hit this all the time, because every volley ends with `m_kBackupFlyTarget.Clear()`. 2. Leftover queued targets Extra targets for multi-target skills go into `m_kQue_kFlyTarget`. When a new cast starts, only the main target is replaced, both locally (`SetFlyTargetInstance`) and for other players (`RecvFlyTargetingPacket`). The queue and the backup are left alone. If the previous volley didn't use up its queue, the next cast pops those old targets and its bolts jump to mobs from the earlier cast. Fix [Hidden Content] I should mention that this fix doesn't change the fact that if a skill can hit more targets than there are mobs in range, vanilla fires each spare bolt 80m out in a random direction within +-10 degrees of your target (`NEW_GetRandomPositionInFanRange`). That's by design but can be easily changed.
      • 31
      • Metin2 Dev
      • Flame
      • kekw
      • Love
      • muscle
  2. Implemented Spectator Panel The tournament now comes with a dedicated spectator interface, allowing players to follow all active arenas directly from the lobby. Opens automatically during round preparation for players outside the arenas. Displays contestant names, scores, duel numbers, match status, and win/lose indicators. Updates HP and MP in real time, including recovery effects from regular and automatic potions. Tracks round preparation, duel starts, lobby returns, and the next round or heat. Shows each arena’s anti-stall timer, duel reset, next-duel countdown, disconnect grace, and reconnect timers. Displays current and total rounds/heats, alongside the applicable victory rules: best-of, required lead, and hard limits. Clears completed arenas when contenders return to the lobby and resizes according to the remaining matches. Supports smooth scrolling with clipping for additional arenas. Players can move the window, close it, and reopen it through the Tournament button. The UI has since then been improved with seamless Clip Masking compared to the video showcase.
  3. 1. Delete your data/monster folder. 2. Take your client files pertaining to npc,monster,monster2,npc_mount etc, and merge them into a simple /monster folder. If there's duplicates, keep the newest ones. 3. Remove the msm and dds, alongside the gr2 files. So leave just the motlist and animation files. 4. Paste the resulting folders in the now empty data/monster. 5. Compare each monster's folder field in proto with the one in npclist, relative to data. Profit.
  4. This is data mismatch with client. The client moves them at a certain speed, the server at another. If they don't match you get stuff like this. Where you'll damage them only when the server catches up to the client. Spawning them with /ma spawns them in position, client and server already match since they're not moving or they're very close to you. TL;DR: Update your share/data folder to match your client's. Also not probable but make sure Sohan monsters use the correct folder in the proto.
  5. Very useful, many thanks for the heads-up. Something to point out is that the problem is not the destructor by itself as much as it is the combination of an owning raw pointer plus compiler-generated memberwise copying. A destructor still allows implicit copy operations, but prevents implicit move operation. Also, In copy assignment, saying the destructor does not run is.. technically true, but slightly imprecise: assignment never invokes the destination object's destructor. The bug is that its currently owned buffer is overwritten without an explicit buffer_delete, creating a leak before the eventual double-return/corruption. The analysis is accurate. After the same pooled buffer is deleted twice: pool[pi] = b; b->next = b; The first later buffer_new() returns b while leaving pool[pi] still pointing at it, meaning it resets b->next to nullptr and the second buffer_new() then returns that exact same b again. So two apparently separate owners receive one allocation. The LS result in the mock is expected, but it is not directly detecting the double-return. x and y are raw pointers that are never passed to buffer_delete; because both refer to the same allocation, LS reports one buffer object plus its 8192-byte payload as leaked. Simply adding assert(x != y) or something similiar makes the allocator corruption visible immediately. Mock: [Hidden Content] For a prevention-only fix, deleting copies and implementing moves is the right choice: TEMP_BUFFER(const TEMP_BUFFER&) = delete; TEMP_BUFFER& operator=(const TEMP_BUFFER&) = delete; TEMP_BUFFER(TEMP_BUFFER&& other) noexcept : buf(std::exchange(other.buf, nullptr)) , forceDelete(std::exchange(other.forceDelete, false)) { } TEMP_BUFFER& operator=(TEMP_BUFFER&& other) noexcept { if (this != &other) { buffer_delete(buf); buf = std::exchange(other.buf, nullptr); forceDelete = std::exchange(other.forceDelete, false); } return *this; } but the deep-copy assignment example you proposed has one meaningful weakness: it deletes the current buffer before allocating and filling the replacement. If allocation or buffer_write can fail, buf may remain dangling or the object may be left partially modified. A copy-and-swap approach is preferable: void swap(TEMP_BUFFER& other) noexcept { using std::swap; swap(buf, other.buf); swap(forceDelete, other.forceDelete); } TEMP_BUFFER& operator=(const TEMP_BUFFER& other) { if (this != &other) { TEMP_BUFFER copy(other); swap(copy); } return *this; } That is only safe if the copy constructor itself correctly handles allocation/write failure according to the real buffer_new and buffer_write contracts, and the deep-copy constructor must preserve every semantically relevant buffer field. Copying only write_point_pos is fine only if TEMP_BUFFER intentionally represents a fresh writable snapshot. If it can wrap a partly-read packet, it should also preserve read position and any other state that affects interpretation. If TEMP_BUFFER has unique ownership and no meaningful use case where you have to duplicate a buffer, making it move-only is the better design imo. struct TEMP_BUFFER { LPBUFFER buf{nullptr}; bool forceDelete{false}; TEMP_BUFFER(const TEMP_BUFFER&) = delete; TEMP_BUFFER& operator=(const TEMP_BUFFER&) = delete; TEMP_BUFFER(TEMP_BUFFER&& other) noexcept : buf(std::exchange(other.buf, nullptr)) , forceDelete(std::exchange(other.forceDelete, false)) { } TEMP_BUFFER& operator=(TEMP_BUFFER&& other) noexcept { if (this != &other) { buffer_delete(buf); buf = std::exchange(other.buf, nullptr); forceDelete = std::exchange(other.forceDelete, false); } return *this; } ~TEMP_BUFFER() { buffer_delete(buf); } }; Copy semantics are only required where code genuinely needs an independent duplicate, for example: TEMP_BUFFER b = a; Passing an lvalue TEMP_BUFFER by value. Storing it in an API or container operation that copies elements. Returning or assigning from an lvalue where a move is not requested. Normal return-by-value code does not usually require copying because NRVO or move construction handles it. Standard containers can also hold move-only types, provided the element is movable and preferably noexcept movable.
  6. The motion files for the boss on the serverside probably got a degree of knockback, while your clientsided ones don't. The server thinks you moved, so the boss moves with you, but doesn't reflect the change on the client because of this mismatch. When you move, you refresh your position, so the boss comes back and the cycle repeats itself.
  7. We already have mysql_direct_query. every modern server pretty much uses this btw.
  8. item_proto is read from the locale in 99.99% of the cases. Dunno why you would have it in your root.
  9. You only need to change those, so if it doesn't work, either: 1. The tool doesn't work. 2. You didn't update your current proto with the new one after generating it. 3. You didn't repack the file after updating it. 4. Your client is reading a different proto from a different path (could be if you're using multilanguage)
  10. ..As I said, values working in game but not on the tooltip (the preview you mentioned) can only mean the clientside proto wasn't updated properly to reflect the value changes you did serverside.
  11. That's not how it works. The client wouldn't start if your proto wasn't using the proper proto structure your binary reads, not if the items have mismatching values in their columns compared to the server.
  12. It means the clientsided itemproto doesn't have the change you made.
  13. I'm gonna hold your hand when I say this, but the old one was a bug.
  14. Literally just add setdelay(0) at the beginning of say_title function, in questlib.lua. It's not necessary to overcomplicate things.
×
×
  • 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.