Jump to content

PetePeter

Member
  • Posts

    106
  • Joined

  • Last visited

  • Feedback

    0%

About PetePeter

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

PetePeter's Achievements

Community Regular

Community Regular (8/16)

  • Conversation Starter
  • Dedicated
  • Reacting Well
  • First Post
  • Collaborator

Recent Badges

36

Reputation

  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
×
×
  • 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.