PetePeter
-
Posts
106 -
Joined
-
Last visited
-
Feedback
0%
Content Type
Forums
Store
Third Party - Providers Directory
Feature Plan
Release Notes
Docs
Events
Posts posted by PetePeter
-
-
20 hours ago, Lycawn said:
yeah you are probably right this code will work fine for small respawn time
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
-
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.
- 1
-
56 minutes ago, Mafuyu said:
everything in spanish dude cmon
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;
- 2
- 1
-
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
-
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
- 1
- 2
-
2 hours ago, Draveniou1 said:
My post updated
i add version 3 full fix
Changelog
1. Fix quest pick in quest
2. Fix delayed desc kick
Now it works stable without problems in version 3
thanks a lot
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); }
- 1
-
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); }
- 1
- 1
-
1 minute ago, Syreldar said:
quest drops begin state start begin function GetDropData(vnum) return ({ [8002] = { {["vnum"] = 101051, ["quantity"] = 1, ["perc"] = 100}, {["vnum"] = 101001, ["quantity"] = 1, ["perc"] = 100}, {["vnum"] = 71151, ["quantity"] = 5, ["perc"] = 100}, -- more drops here. }, -- more monsters here. })[vnum]; end -- function when kill with not npc.is_pc() and npc.get_race() == 8002 and math.abs(pc.get_level() - npc.get_level()) <= 15 begin local drop_data = drops.GetDropData(npc.get_race()); for _, item_data in ipairs(drop_data) do if (math.random(100) <= item_data["perc"]) then game.drop_item_with_ownership(item_data["vnum"], item_data["quant"]); --break; -- Uncomment if you want to limit to one drop per monster kill. end -- if end -- for end -- when end -- state end -- quest
Have a reason if the whole game is not in LUA
- 4
- 1
-
23 minutes ago, Draveniou1 said:
it has been tested before I publish it
What is the difference between sequence and this here?
Still some are trying to find the key try to find the key and come tell me if troll
I am waiting
If no difference why using this ? You don't get the point I think
-
4 minutes ago, Draveniou1 said:
We are trying to find the key
The key cannot be found
you can find me the key and then come laugh
we have already tested on big server before publishing here
Impossible than you are not trolling lol
-
3 minutes ago, Maseda said:
And example for metin.txt , mob.txt, tanaka.txt you can show us??
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 -
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 ?
-
@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
- 1
-
33 minutes ago, kaJaMrSimple said:
https://metin2.download/video/LbSJGPh5DOoRj4Mr431vNvz2N6kAp9jQ/.mp4
Is the situation in this video related to what you said then? because it was not after the shutdown, but in the flowing game for many players.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
-
1 hour ago, kaJaMrSimple said:
Hi,
It's a very rare situation. mobs don't move at all. but they keep hitting. fixed by turning the client off and on.
gif : https://metin2.download/picture/4bKgLtxayPTt6CZEw4PK88tA8DXHdII6/.gif
no client and server syserr.
Is there anywhere I should check?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
-
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
-
2 minutes ago, Mefarious said:
Hello, is there anyone who knows what can cause a problem with mob animation?
I mean, when i attack mob hes visibkle model doesnt move but i recieve damage, its like his invisible object chasing me but his visible model/animation stays in place.
It started to occur today, i never had such a problem.
I deleted all quest because i thought they could caused it but even reboot/restart server wont help me.Restart your client, it's happen after some shutdown while the client is open
-
1 hour ago, caanmasu said:
Alternative (no C++, only lua)
party.give_item2 = function(item_vnum, item_count) if party.is_party() then local pids = {party.get_member_pids()} for _, pid in next, pids, nil do q.begin_other_pc_block(pid) pc.give_item2(item_vnum, item_count) q.end_other_pc_block() end party.get_member_pids() else pc.give_item2(item_vnum, item_count) end end
Advantage:
You can to use in dungeon or not.
Usage (example):
party.give_item2(27001, 200)
quest_functions: party.give_item2
¡Saludos!
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
- 1
-
3 hours ago, TAUMP said:
I dont see what is fixed, wtf
1 hour ago, Theboost said:I have seen your gif but can you explain ?
When you use this skill with a warrior you can see your HP dropping and coming back to normal
- 1
- 1
-
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_functiond.give_item2
And finally in your quest
d.give_item2(vnum, count)
- 1
-
-
43 minutes ago, bunker said:
Hmm I haven't touched the InventoryWindow.py
In my client I edited:
- added icon/72319.tga
- added icon/72320.tga
- edited locale/de/item_list.txt
- edited locale/de/itemdesc.txt
- edited locale/de/locale_game.txt
- edited root/game.py
- edited root/interfacemodule.py
- edited root/uicommon.py
- edited root/uiinventory.py
- added etc/ymir work/ui/ex_inven_cover_button_close.sub
- added etc/ymir work/ui/ex_inven_cover_button_open.sub
- replaced etc/ymir work/ui/windows.dss
EDIT: ohh.. how do I edit the item_proto in my locale/de/ folder?
EDIT2: Got it to work (dump_proto) now it looks like this - much better:
So you have to edit the "InventoryWindow.py" this is the ui (icons, positions, etc ...)
-
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/... -
5 minutes ago, bunker said:
Thank you for your answer. I still couldn't figure it out.
I checked the item_proto, everything seems fine to me with that.
For the other part, I'm not quite sure what you mean.
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
SungmaHee Tower Official Servers (C++, Python)
in Features & Metin2 Systems
Posted
Your "mood" is not an excuse anyway. Use this link to get all informations: https://it-wiki.metin2.gameforge.com/index.php/Inferna_Sung_Mahi