Jump to content

PetePeter

Member
  • Posts

    106
  • Joined

  • Last visited

  • Feedback

    0%

Everything posted by PetePeter

  1. Your "mood" is not an excuse anyway. Use this link to get all informations: [Hidden Content]
  2. The code will only work if the player don't teleport or any other action after killed the dragon. You use simple timer which is linked to the player and not server side, so it's even more useless than it's look like
  3. That's cool thanks ! What if we use Python's `ast` (Abstract Syntax Tree) module to handle the substitution of dynamic variable names in the code? I mean, we could technically parse the Python source file and replace all possible variables. Check this out, I've added a function that handles dynamic names in simple cases, specifically looking for Call nodes and Attribute nodes that could resemble a dynamic variable name access. Though it's not a perfect solution, it can cover many simple scenarios: from ast import parse, Import, ImportFrom ... class SourceFile: ... def read(self): ... if file_name.lower() == module.lower(): self.regex_pattern = self.create_regex_pattern(content, file_name) self.modules_constants[module] = findall( self.regex_pattern, content, IGNORECASE ) ... @staticmethod def create_regex_pattern(content: str, module: str) -> str: """Create regex patterns for different types of imports""" module_mapping = SourceFile.get_import_aliases(content) if module in module_mapping: return r"\b" + escape(module_mapping[module]) + r"\.([a-zA-Z_]\w*)(?![\[\(])\b" else: return r"\b" + escape(module) + r"\.([a-zA-Z_]\w*)(?![\[\(])\b" @staticmethod def get_import_aliases(content: str) -> dict[str, str]: """Parse the file and get the aliases for imports""" root = parse(content) import_aliases = {} for node in ast.walk(root): # Use the python built-in library AST to parse the python file if isinstance(node, Import): # check for import statements for name in node.names: module = name.name alias = name.asname if name.asname else name.name import_aliases[module] = alias elif isinstance(node, ImportFrom): # check for from import statements for name in node.names: module = node.module alias = name.asname if name.asname else name.name import_aliases[module] = alias return import_aliases import ast class SourceFile: ... @staticmethod def handle_dynamic_names(content: str, module: str): root = ast.parse(content) dynamic_names = [] for node in ast.walk(root): if isinstance(node, ast.Call) and getattr(node.func, "id", "") == "getattr": module_obj = node.args[0] attr_obj = node.args[1] if isinstance(module_obj, ast.Subscript) and isinstance(attr_obj, ast.Str): if module_obj.value.id == "globals" and attr_obj.s == module: dynamic_names.append(node.func.attr) return dynamic_names def read(self): ... dynamic_names = self.handle_dynamic_names(content, module) for dynamic_name in dynamic_names: self.modules_constants[module].append(dynamic_name) ... Now, this might not always work perfectly, especially for more complex cases. For example, situations where attribute names are generated through loops, or via mathematical operations could throw a spanner in the works. Basically, when the attribute name ain't directly visible in the script, this approach could miss it. But all in all, this seems like a step in the right direction. Curious to hear your thoughts on this.
  4. WITH items AS ( SELECT *, md5(CONCAT_WS(',',attrtype0, attrvalue0, attrtype1, attrvalue1, attrtype2, attrvalue2, attrtype3, attrvalue3, attrtype4, attrvalue4)) unique_key FROM player.item WHERE attrvalue2>0 AND attrvalue3>0 AND `window` not IN ('SAFEBOX','MALL') AND attrtype0 IN (71,72) ), duplicates AS ( SELECT unique_key, COUNT(*) clones FROM items GROUP BY unique_key HAVING COUNT(*)>1 ), owners AS ( SELECT a.login, p.last_play, p.name player_name, ii.empire, g.name guild, gm.is_general, pp.locale_name, i.unique_key hash_, d.clones duplicates, i.* FROM items i INNER JOIN player.item_proto pp ON pp.vnum = i.vnum INNER JOIN player.player p ON p.id = i.owner_id INNER JOIN duplicates d ON d.unique_key = i.unique_key INNER JOIN player.guild_member gm ON gm.pid = p.id INNER JOIN player.guild g ON g.id = gm.guild_id INNER JOIN `account`.account a ON a.id = p.account_id INNER JOIN player.player_index ii ON ii.id = a.id ) SELECT * FROM owners; But I suggest something like that instead: SELECT a.login, p.last_play, p.name player_name, ii.empire, g.name guild, gm.is_general, pp.locale_name, i.unique_key hash_, d.clones duplicates, i.* FROM ( SELECT *, md5(CONCAT_WS(',', attrtype0, attrvalue0, attrtype1, attrvalue1, attrtype2, attrvalue2, attrtype3, attrvalue3, attrtype4, attrvalue4)) unique_key FROM player.item WHERE attrvalue2 > 0 AND attrvalue3 > 0 AND `window` NOT IN ('SAFEBOX', 'MALL') AND attrtype0 IN (71, 72) ) i INNER JOIN player.item_proto pp ON pp.vnum = i.vnum INNER JOIN player.player p ON p.id = i.owner_id INNER JOIN ( SELECT unique_key, COUNT(*) clones FROM ( SELECT md5(CONCAT_WS(',', attrtype0, attrvalue0, attrtype1, attrvalue1, attrtype2, attrvalue2, attrtype3, attrvalue3, attrtype4, attrvalue4)) unique_key FROM player.item WHERE attrvalue2 > 0 AND attrvalue3 > 0 AND `window` NOT IN ('SAFEBOX', 'MALL') AND attrtype0 IN (71, 72) ) tmp GROUP BY unique_key HAVING COUNT(*) > 1 ) d ON d.unique_key = i.unique_key INNER JOIN player.guild_member gm ON gm.pid = p.id INNER JOIN player.guild g ON g.id = gm.guild_id INNER JOIN `account`.account a ON a.id = p.account_id INNER JOIN player.player_index ii ON ii.id = a.id;
  5. I suggest you to do like that instead of create many state, as the vnum have a pattern: local item1, item2 local levelgive = math.floor((pc.get_level() - 10) / 10) * 10 if pc.get_race() == WARRIOR_M or pc.get_race() == WARRIOR_W then item1 = 21900 item2 = 21903 elseif pc.get_race() == NINJA_W or pc.get_race() == NINJA_M then item1 = 21901 item2 = 21902 elseif pc.get_race() == SURA_M or pc.get_race() == SURA_W then item1 = 21900 elseif pc.get_race() == SHAMAN_W or pc.get_race() == SHAMAN_M then item1 = 21904 item2 = 21905 end local totalitem = item2 and 2 or 1 if item1 then item1 = item1 + levelgive end if item2 then item2 = item2 + levelgive end say() if item1 then say_item_vnum_inline(item1, 0, totalitem) end if item2 then say_item_vnum_inline(item2, 1, totalitem) end
  6. It's not about "normal" ways. 1. You didn't add any return to the function after the player is kicked, so the rest of the function is executed normally 2. You never reset the counter, it's only reset after a kick as the script continue his path and reach the "if (NewFixGuildCommente >= 3)" again 3. (Optionnal) Consider using smart pointers (like std::unique_ptr) instead of raw pointers for better memory management and exception safety. I know the sql function is not made by you, but if we edit this function we can optimise everything also
  7. Did you recheck your code ? Here is a possible solution: void CGuild::DeleteComment(LPCHARACTER ch, DWORD comment_id) { const int maxAttempts = 3; const int resetIntervalMinutes = 10; // Change this to the desired interval in minutes time_t currentTime = time(0); int attempts = ch->GetQuestFlag("Newfixxed.Newguildcomment"); time_t lastAttemptTime = ch->GetQuestFlag("Newfixxed.LastAttemptTime"); // If the user's last attempt was longer than the reset interval ago, reset their attempts if (difftime(currentTime, lastAttemptTime) >= resetIntervalMinutes * 60) { attempts = 0; } // Check if the user has reached the maximum number of attempts if (attempts >= maxAttempts) { ch->GetDesc()->DelayedDisconnect(0); return; } std::unique_ptr<SQLMsg> pmsg; // Execute the appropriate query based on the user's guild grade if (GetMember(ch->GetPlayerID())->grade == GUILD_LEADER_GRADE) { pmsg.reset(DBManager::instance().DirectQuery("DELETE FROM guild_comment WHERE id = %u AND guild_id = %u", comment_id, m_data.guild_id)); } else { pmsg.reset(DBManager::instance().DirectQuery("DELETE FROM guild_comment WHERE id = %u AND guild_id = %u AND name = '%s'", comment_id, m_data.guild_id, ch->GetName())); } uint32_t affectedRows = pmsg->Get()->uiAffectedRows; // Check if the comment was successfully deleted if (affectedRows == 0 || affectedRows == (uint32_t)-1) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 삭제할 수 없는 글입니다.")); } else { RefreshCommentForce(ch->GetPlayerID()); } // Update the user's attempt count and last attempt time ch->SetQuestFlag("Newfixxed.Newguildcomment", attempts + 1); ch->SetQuestFlag("Newfixxed.LastAttemptTime", currentTime); }
  8. Or maybe something like that instead no ? void CGuild::AddComment(LPCHARACTER ch, const std::string& str) { if (str.length() > GUILD_COMMENT_MAX_LEN || str.length() == 0) // Added string null verification return; char text[GUILD_COMMENT_MAX_LEN * 2 + 1]; DBManager::instance().EscapeString(text, sizeof(text), str.c_str(), str.length()); // Fetch the number of existing comments and delete the oldest one if there are 12 comments. DBManager::instance().FuncQuery(std::bind(&CGuild::HandleCommentCount, this, ch, text, std::placeholders::_1), "SELECT COUNT(*), MIN(time) FROM guild_comment%s WHERE guild_id = %u", get_table_postfix(), m_data.guild_id); } void CGuild::HandleCommentCount(LPCHARACTER ch, const char* text, MYSQL_RES* result) { MYSQL_ROW row = mysql_fetch_row(result); if (!row) return; int commentCount = 0; sscanf(row[0], "%d", &commentCount); if (commentCount >= 12) { // Delete the oldest comment DBManager::instance().Query("DELETE FROM guild_comment%s WHERE guild_id = %u AND time = '%s'", get_table_postfix(), m_data.guild_id, row[1]); } // Insert the new comment DBManager::instance().FuncAfterQuery(void_bind(std::bind1st(std::mem_fun(&CGuild::RefreshCommentForce), this), ch->GetPlayerID()), "INSERT INTO guild_comment%s(guild_id, name, notice, content, time) VALUES(%u, '%s', %d, '%s', NOW())", get_table_postfix(), m_data.guild_id, ch->GetName(), (text[0] == '!') ? 1 : 0, text); }
  9. Man it's been 9 years lol Also don't use the quest like that, there is not GM check on the button/info trigger which can be start by everyone
  10. So you remove _improved_packet_encryption_ which include sequence system already just to make a new one from scratch ? What's the benefit of that instead of use packet encryption ?
  11. @echo off set "search_folder=C:\pc\ymir work\etc" set "extension=.dds" set "output_folder=C:\output_folder" xcopy "%search_folder%\*%extension%" "%output_folder%\" /s /i
  12. This is different situation, I'm speaking about the monsters not moving at all. This must be a sync problem because of the mount, the clientside speed and serverside speed are different
  13. Nothing to worry about, it's happen only when you not restart your client after a server shutdown. It's not happen to players as the shutdown close every client expect for GameMaster
  14. Just use the OnUpdate built in function, example: def SetSleepFunction(self, time): self.function_sleep_time = app.GetGlobalTimeStamp() + time def OnUpdate(self): if self.function_sleep_time and self.function_sleep_time < app.GetGlobalTimeStamp(): self.function_sleep_time = None # Your action I write the code very fast for you understand the idea, but you need to declare the variable also and adapt it for you
  15. Restart your client, it's happen after some shutdown while the client is open
  16. Not true, work only with party member. If you have any special dungeon it's will not work (Like guild or public). And it's really not a good way to use "q.begin_other_pc_block" everytime
  17. When you use this skill with a warrior you can see your HP dropping and coming back to normal
  18. questlua_dungeon.cpp ALUA(dungeon_give_item) { if (!lua_tonumber(L, 1)) return 0; int count = 1; if (lua_tonumber(L, 2) > 1) count = lua_tonumber(L, 2); CQuestManager& q = CQuestManager::instance(); LPDUNGEON pDungeon = q.GetCurrentDungeon(); if (pDungeon) pDungeon->GiveItem(lua_tonumber(L, 1), count); return 0; } -------------------------------------------------- {"give_item2", dungeon_give_item}, dungeon.cpp namespace { struct FGiveItem { FGiveItem(int vnum, int count) : m_vnum(vnum), m_count(count) { } void operator() (LPENTITY ent) { if (ent->IsType(ENTITY_CHARACTER)) { LPCHARACTER ch = (LPCHARACTER) ent; if (ch && ch->IsPC()) { int iCount = m_count; //ch->ChatPacket(CHAT_TYPE_COMMAND, "%s", m_psz); const auto& item = ITEM_MANAGER::instance().CreateItem(m_vnum, m_count, 0, true); if (item->IsStackable()) { ch->AutoGiveItem(m_vnum, m_count); } else { while (iCount > 0) { ch->AutoGiveItem(m_vnum, 1); iCount -= 1; } } ITEM_MANAGER::instance().DestroyItem(item); } } } int m_vnum; int m_count; }; } void CDungeon::GiveItem(int vnum, int count) { sys_log(0, "XXX Dungeon GiveItem %p %d %d", this, vnum, count); LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap(m_lMapIndex); if (!pMap) { sys_err("cannot find map by index %d", m_lMapIndex); return; } FGiveItem f(vnum, count); pMap->for_each(f); } dungeon.h public: void GiveItem(int vnum, int count); quest_function d.give_item2 And finally in your quest d.give_item2(vnum, count)
  19. So you have to edit the "InventoryWindow.py" this is the ui (icons, positions, etc ...)
  20. I think you have edited the file "InventoryWindow.py" inside uiscript folder instead of "locale/xx/ui/InventoryWindow.py". Try to follow the tutorial for this part again. For the item_proto, navicat it's serverside. You have to edit the item_proto clientside (inside the locale/xx/ folder) PS: The xx mean the language code, like de/en/fr/it/...
  21. Attach your uiinventory.py here so I can show you what I mean. The item_proto is not only the .txt by the way, you have to compile "item_proto.txt" / "item_names.txt" to "item_proto" file
×
×
  • 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.