Jump to content

Karbust

Management
  • Posts

    1161
  • Joined

  • Days Won

    10
  • Feedback

    100%

Posts posted by Karbust

  1. On 3/22/2022 at 7:59 PM, Anohros said:

    Regarding the bone errors.. my granny converter library already addresses this kind of issue. However it was not exposed as an extra option until now.. **Update** Now, it only requires to enable an option which can be set straightforwardly.

    You could implement a checkbox for the option in your GUI.

    I just updated my library to provide the option for this which can be set straightforwardly. I also updated the example.

    Just review my recent commit:

    This is the hidden content, please

    Without those changes: https://metin2.download/picture/KEsNS2jtTUTXw32WZKAw6YfAegY24ig8/.gif

    With those changes: https://metin2.download/picture/fofDIklu9ykjoR2cBqPu1i3a3n8W9765/.gif

    Code used (with it false that weird flying bug doesn't happen):

        GCL::Importer::GrannyImportOptions options;
        options.importAnimationDeboor = true;
        GrannyImporter* importer = new GrannyImporter(options);

    I attempted to apply that update to see if it would solve the left shoulder issue, but apparently it didn't.

    To fix it manually just need to mirror Bone_shoulder_03 on XY axis and adjust the position manually.

    • Love 1
  2. GitHub:

    This is the hidden content, please
     => (or
    This is the hidden content, please
    )

    This tutorial is also available on the GitHub repo.

    Metin2-InfluxDB

    The purpose of this is to be able to create dashboards with the timeseries statistics, like the ones seen on the video made with Grafana.

    Install InfluxDB: https://docs.influxdata.com/influxdb/v2.1/install/

    ℹ️ Information

    If you intend on using Cloudflare (and nginx as reverse proxy, since InfluxDB’s default port is 8086, which isn’t supported by Cloudflare, if you choose the docker installation, just map it to a port supported by Cloudflare, preferably HTTPS), you will need to create a firewall exception to bypass all rules that come from your metin2 game server so it doesn’t get blocked or challenged.

    I recommend setting up nginx as reverse proxy, that way the port 443 can be used for all hosts configured on nginx.

    On FreeBSD, only InfluxDB 1.8.* is available, not recommended, since the latest major stable version is 2.1. This tutorial was made for version 2.1, there may be differences on the requests.

    An IP address can be used, but a domain with SSL setup is preferable, for obvious reasons.

    InfluxDB Setup

    Create an organization

    .png

    Create a bucket

    .png

    Lib cURL

    FreeBSD Setup

    Install it:

    pkg install curl

    And link it on the Makefile:

    -lcurl

    Windows Setup

    For Windows it works with both vcpkg and compiling the lib manually.

    Shared lib:

    vcpkg install curl[openssl]:x86-windows

    Static lib:

    vcpkg install curl[openssl]:x86-windows-static

    Auto Event Manager

    In case you already have this class/file, just compare and make the necessary changes. All the changes related to Influx are wrapped with the macro INFLUX_STATS.

    Don't forget to also make the necessary changes on main.cpp. If you are already using this you won't need to include the header and initialize the class.

    Edit Time

    The default is send the data every 10 minutes.

    On the file InfluxData.cpp, edit this code:

    void CInfluxData::Check(int day, int hour, int minute, int second) {
        int lastNumber = minute % 10;
    
        if (isInfluxEnabled && lastNumber == 0 && second == 00)

    If lastNumber = 0, then it means the number is divisible by, in this case, 10. Make sure when you edit the time to don't forget the seconds part, if you don't use it, it will run every second inside the minute you want to run it.

    Examples for other times

    Every minute

        if (isInfluxEnabled && second == 00)

    Every 5 minutes

        int lastNumber = minute % 5;

    Every 30 seconds

        int lastNumber = seconds % 30;
    
        if (isInfluxEnabled && lastNumber == 0)

    Configuration

    To use this make sure you have it only enabled on ONE core per channel, the online counter inside the channel is common between the cores, just add this on the CONFIG file to enable Influx on the core.

    INFLUX_ENABLE: 1

    The rest of the settings, can either be set on the source or by CONFIG options (this are the default values set on the source):

    INFLUX_URL: http://127.0.0.1:8086
    INFLUX_TOKEN: <token>
    INFLUX_ORG: <organization>
    INFLUX_BUCKET: <bucket>

    NodeJS Version

    In case you don't to use the source, a NodeJS version is available where you can use the Metin2 API to get the user count and send it to Influx.

    The source method is preferred.

    Acknowledgments:

    Gurgarath for testing the tutorial on Windows.

    Deliris for testing the tutorial on FreeBSD.

    • Metin2 Dev 27
    • Confused 1
    • Good 10
    • Love 2
    • Love 9
  3. 1 minute ago, lime said:

    I don't want to be rude, but I wouldn't like to hear some kids raging at me while I'm chill farming and destroying stones.

    Yes, it would be great if you could mute specific people with a button near the `Whisper` button on the upper side of the window, or if you could disable the function completely, or if you can do it Group/Guild only. 

    Is portaudio part of this project?

     

    I'll give it a yes for the system, no for buying such thing. 

    For now, I only have it enabled on groups.

    I'm also working on an implementation that would allow the users to enable voice on an entire map, like a commerce map, or whatever.

    Users can mute/unmute themselves. Push-To-Talk will also be possible.

    • Good 1
  4. I'm doing a system that offers voice ingame, it is almost done. Would anyone consider buying a system that would offer Voice Ingame?

    Leave your feedback please.

    Thank you

    • Scream 1
    • Love 1
  5. Sup guys

     DISCLAIMER: The code on this topic of only useful if have done changes to the Pet System (like putting it on the slot) and/or use the Mount System that follows the character if you have it on a slot. The code for the horse is valid for everyone.

     

    This code was done by @ Mali and he asked me to share it.

     

    This code is to fix a bug on observer mode, like guild wars.

    • If you have a pet on the slot and it respawns every time you teleport, this affects you.
    • If you have a mount on the slot and it respawns every time you teleport, this affects you.
    • If you only have the mount following you and you unmount while on observer, it will affect you.
    • This last one is also valid of the horse.

    The bug consists on having any of the points described above which will make the pet, mount or horse visible to everyone when the player is in observer mode.

    Something like this (the player Faisca is the observer on a guild war):

    Spoiler

    unknown.png

    unknown.png

     

    Let's start

    Open char_horse.cpp, and after:

    Spoiler
    m_chHorse->SetRider(this);

    Add:

    Spoiler
    m_chHorse->SetObserverMode(IsObserverMode());

    Open PetSystem.cpp

    Search for:

    Spoiler
    	if (0 != m_pkChar)
    	{
    		m_pkChar->Show (m_pkOwner->GetMapIndex(), x, y);
    		m_dwVID = m_pkChar->GetVID();
    
    		return m_dwVID;
    	}

    And make it like this:

    Spoiler
    	if (0 != m_pkChar)
    	{
    		m_pkChar->Show (m_pkOwner->GetMapIndex(), x, y);
    		m_dwVID = m_pkChar->GetVID();
    
    		m_pkChar->SetObserverMode(m_pkOwner->IsObserverMode());
    
    		return m_dwVID;
    	}

    Search for:

    Spoiler
    m_pkChar->Show(m_pkOwner->GetMapIndex(), x, y, z);

    Add after: 

    Spoiler
    m_pkChar->SetObserverMode(m_pkOwner->IsObserverMode());

    At the end of the file add this:

    Spoiler
    void CPetSystem::UpdateObserver()
    {
    	if (!m_pkOwner)
    		return;
    
    	const bool bFlag = m_pkOwner->IsObserverMode();
    
    	for (TPetActorMap::const_iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter)
    	{
    		const CPetActor* petActor = iter->second;
    		if (petActor == nullptr)
    			continue;
    		
    		const LPCHARACTER petCh = petActor->GetCharacter();
    		if (petCh)
    			petCh->SetObserverMode(bFlag);
    	}
    }

    Open PetSystem.h, and after:

    Spoiler
    void		DeletePet(CPetActor* petActor);

    Add:

    Spoiler
    void		UpdateObserver();

    Open entity.cpp

    Inside this function:

    Spoiler
    void CEntity::SetObserverMode(bool bFlag)

    Change it like this:

    Spoiler
    	if (IsType(ENTITY_CHARACTER))
    	{
    		LPCHARACTER ch = (LPCHARACTER) this;
    
    		LPCHARACTER chHorse = ch->GetHorse();
    		if (chHorse)
    			chHorse->SetObserverMode(bFlag);
    
    		CPetSystem* petSystem = ch->GetPetSystem();
    		if (petSystem)
    			petSystem->UpdateObserver();
    
    		ch->ChatPacket(CHAT_TYPE_COMMAND, "ObserverMode %d", m_bIsObserver ? 1 : 0);
    	}

     

    You are finished if you don't use a special mount system.

     

    Now, if you have the file MountSystem.cpp, open it, and search for:

    Spoiler
    	if (0 != m_pkChar)
    	{
    		m_pkChar->Show(m_pkOwner->GetMapIndex(), x, y);
    		m_dwVID = m_pkChar->GetVID();
    
    		return m_dwVID;
    	}

     

    And edit like this:

    Spoiler
    	if (0 != m_pkChar)
    	{
    		m_pkChar->Show(m_pkOwner->GetMapIndex(), x, y);
    		m_dwVID = m_pkChar->GetVID();
    
    		m_pkChar->SetObserverMode(m_pkOwner->IsObserverMode());
    
    		return m_dwVID;
    	}

    Search for:

    Spoiler
    m_pkChar->Show(m_pkOwner->GetMapIndex(), x, y, z);

    Add after:

    Spoiler
    m_pkChar->SetObserverMode(m_pkOwner->IsObserverMode());

    At the end of the file add this:

    Spoiler
    void CMountSystem::UpdateObserver()
    {
        for (TMountActorMap::const_iterator iter = m_mountActorMap.begin(); iter != m_mountActorMap.end(); ++iter)
        {
            const CMountActor* mountActor = iter->second;
            if (mountActor == nullptr)
                continue;
    
            const LPCHARACTER mountCh = mountActor->GetCharacter();
            if (mountCh)
                mountCh->SetObserverMode(m_pkOwner->IsObserverMode());
        }
    }

    Open MountSystem.h, and after:

    Spoiler
    void		DeleteMount(CMountActor* mountActor);

    Add:

    Spoiler
    void		UpdateObserver();

    Then edit entity.cpp again like this:

    Spoiler
    	if (IsType(ENTITY_CHARACTER))
    	{
    		LPCHARACTER ch = (LPCHARACTER) this;
    
    		LPCHARACTER chHorse = ch->GetHorse();
    		if (chHorse)
    			chHorse->SetObserverMode(bFlag);
    
    		CPetSystem* petSystem = ch->GetPetSystem();
    		if (petSystem)
    			petSystem->UpdateObserver();
    
    		CMountSystem* mountSystem = ch->GetMountSystem();
    		if (mountSystem)
    			mountSystem->UpdateObserver();
    
    		ch->ChatPacket(CHAT_TYPE_COMMAND, "ObserverMode %d", m_bIsObserver ? 1 : 0);
    	}

    And your are finished.

    Thanks again to @ Mali for helping me solve this issue.

    • Metin2 Dev 26
    • Lmao 1
    • Good 5
    • Love 1
    • Love 9
  6. 17 hours ago, HattanBinNassar said:

    So informative , thanks a lot ,, my fear of building a website has increased to the roof 🤣

    No one should face fear when starting, we all start somewhere, sometimes is just trial and error.

    I read a lot of blog entries (mostly all on medium) and documentation for something I'm learning or trying to achieve or trying to compare to alternatives and see which one is the most performant and/or more secure.

    I have a lot of books, some which I have personally used for both college and personal projects, you can find them here: https://ebooks.karbust.me/Technology/

    • Love 1
  7. 2 hours ago, HattanBinNassar said:

    Nice , this informative , what do you think as a web dev is the most potential risk of a website ?

    SQL Injection, bad handling of sessions and/or authentication cookies/tokens, remote code execution

    If you don't sanitize every single thing you get from the frontend and it's supposed to end up in a database, then you are open to the risk of SQL Injection. I recommend the use of ORMs, since it already handles almost all the boring part of sanitizing input.

    If you don't write good code and end up doing some sketchy shit that executes commands on the server running the website, then your are fucked...

    Authentication is some of the hard parts of doing a good website, there are discussions on where should the information be saved, either cookies or local storage and manually attach it to every request. I work with JWT, and I always put the token on cookies so it goes attached to every request when CORS with Allow Credentials set to true. Also make sure you use strict true so it doesn't go on requests to other domains. Obviously cookies can be grabbed by some third party trick, but it's hard and if you put a expire date on JWT and use refresh tokens and IP validation, then you are better served.

     

    • Love 1
  8. account.account can have insert, select and update, not only to email and password like you referenced.

    player.guild only select

    player.player can have select and update (in case you have an unbug function, otherwise only select)

    player.item or player.item_award only insert, depending on which table you use

    Other tables related to item shop and purchases should have, at least, insert and select

    • Good 1
  9. 7 hours ago, HattanBinNassar said:

    1 - is it possible to limit the client login attempts to 1 per 10 minutes ?

    2 - is it possible to limit the login & create accounts to 2 per ip ?

    Yes to both questions.

    For the first you need to make changes in the source, somewhere in input_login, maybe, never did it.

    For the second, assuming you are doing it in PHP, I have no idea how to do it, just search for route rate limit in php on google. In NodeJS (ExpressJS or others) would be a lot easier.

  10. I'm having an issue with this and I have no idea what is causing it...

    This is what I was able to gather, the issue is coming from this lines:

    This is the hidden content, please

    I put the code like this:

    					DWORD dwSocket2 = pItemAward->dwSocket2;
    
    					sys_err("Socket2 0: %u", dwSocket2);
    					sys_err("bType: %d", pItemTable->bType);
    					sys_err("bSubType: %d", pItemTable->bSubType);
    
    					if (pItemTable->bType == ITEM_UNIQUE)
    					{
    #ifdef ENABLE_EXTEND_ITEM_AWARD
    						// 12.04.2019 - Correction for unique items based on the real time.
    						const long lValue0 = pItemTable->alValues[ITEM_SOCKET_REMAIN_SEC];
    						const long lValue2 = pItemTable->alValues[ITEM_SOCKET_UNIQUE_REMAIN_TIME];
    						const time_t tNow = CClientManager::instance().GetCurrentTime();
    						dwSocket2 = (lValue2 == 0) ? static_cast<DWORD>(lValue0) : static_cast<DWORD>(tNow + lValue0);
    						sys_err("Socket2 1: %u", dwSocket2);
    #else
    						if (pItemAward->dwSocket2 != 0)
    							dwSocket2 = pItemAward->dwSocket2;
    						else
    							dwSocket2 = pItemTable->alValues[0];
    #endif
    					}

    And this is the output:

    SYSERR: Jan 14 20:31:34 :: RESULT_SAFEBOX_LOAD: Socket2 0: 28435
    SYSERR: Jan 14 20:31:34 :: RESULT_SAFEBOX_LOAD: bType: 16
    SYSERR: Jan 14 20:31:34 :: RESULT_SAFEBOX_LOAD: bSubType: 2
    SYSERR: Jan 14 20:31:34 :: RESULT_SAFEBOX_LOAD: Socket2 1: 180
    SYSERR: Jan 14 20:31:34 :: RESULT_SAFEBOX_LOAD: Query: INSERT INTO item (id, owner_id, item.window, pos, vnum, count, socket0, socket1, socket2, attrtype0, attrvalue0, attrtype1, attrvalue1, attrtype2, attrvalue2, attrtype3, attrvalue3, attrtype4, attrvalue4, attrtype5, attrvalue5, attrtype6, attrvalue6) VALUES(2000000012, 1, 'MALL', 1, 61096, 1, 28433, 28434, 180, 72, 65, 71, 30, 5, 12, 9, 20, 12, 8, 0, 0, 0, 0)

    This is the proto of that item:

    61096	LaminaXPTO	ITEM_WEAPON	WEAPON_TWO_HANDED	3	ANTI_ASSASSIN | ANTI_SURA | ANTI_MUDANG | ANTI_DROP | ANTI_SELL	ITEM_TUNABLE	WEAR_WEAPON	NONE	0	0	0	0	15	LEVEL	240	LIMIT_NONE	0	APPLY_ATT_SPEED	65	APPLY_ATTBONUS_HUMAN	55	APPLY_CRITICAL_PCT	50	0	550	650	600	700	70	0	3	-1															

    This is what I used to create it:

    INSERT INTO `player`.`item_award` (`login`, `vnum`, `count`, `socket0`, `socket1`, `socket2`, `attrtype0`, `attrvalue0`, `attrtype1`, `attrvalue1`, `attrtype2`, `attrvalue2`, `attrtype3`, `attrvalue3`, `attrtype4`, `attrvalue4`,  `mall`)
    VALUES (
    	'admin',	-- LOGIN
    	61096,		-- ITEM ID
    	1,		-- COUNT
    	28433, 28434, 28435,	-- SLOTS
    	72, 65,		-- DM
    	71, 30,		-- DH,
    	5, 12,		-- APPLY_STR
    	9, 20,		-- APPLY_CAST_SPEED
    	12, 8,		-- APPLY_POISON_PCT
    	1		-- LOCATION
    );

    This is the result on item_award:

    id	pid	login	vnum	count	given_time	taken_time	item_id	why	socket0	socket1	socket2	socket3	socket4	socket5	attrtype0	attrvalue0	attrtype1	attrvalue1	attrtype2	attrvalue2	attrtype3	attrvalue3	attrtype4	attrvalue4	attrtype5	attrvalue5	attrtype6	attrvalue6	mall
    19	0	admin	61096	1	1000-01-01 00:00:00				28433	28434	28435	0	0	0	72	65	71	30	5	12	9	20	12	8	0	0	0	0	1

    And this is what I get when I open the mall:

    id	owner_id	window	pos	count	vnum	transmutation	socket0	socket1	socket2	socket3	socket4	socket5	attrtype0	attrvalue0	attrtype1	attrvalue1	attrtype2	attrvalue2	attrtype3	attrvalue3	attrtype4	attrvalue4	attrtype5	attrvalue5	attrtype6	attrvalue6
    2000000012	1	MALL	1	1	61096	0	28433	28434	180	0	0	0	72	65	71	30	5	12	9	20	12	8	0	0	0	0

    The Socket2 is getting fucked up and I have no clue why. I have no idea how the item type is being assumed as ITEM_UNIQUE.

    Anyone has any idea on how to solve it?

    For some reason the same db works on my other server and this issue doesn't happen, the only difference is the mysql database.

    Thank you

     

    16 hours ago, Karbust said:

    I'm having an issue with this and I have no idea what is causing it...

    This is what I was able to gather, the issue is coming from this lines:

    This is the hidden content, please

    I put the code like this:

    					DWORD dwSocket2 = pItemAward->dwSocket2;
    
    					sys_err("Socket2 0: %u", dwSocket2);
    					sys_err("bType: %d", pItemTable->bType);
    					sys_err("bSubType: %d", pItemTable->bSubType);
    
    					if (pItemTable->bType == ITEM_UNIQUE)
    					{
    #ifdef ENABLE_EXTEND_ITEM_AWARD
    						// 12.04.2019 - Correction for unique items based on the real time.
    						const long lValue0 = pItemTable->alValues[ITEM_SOCKET_REMAIN_SEC];
    						const long lValue2 = pItemTable->alValues[ITEM_SOCKET_UNIQUE_REMAIN_TIME];
    						const time_t tNow = CClientManager::instance().GetCurrentTime();
    						dwSocket2 = (lValue2 == 0) ? static_cast<DWORD>(lValue0) : static_cast<DWORD>(tNow + lValue0);
    						sys_err("Socket2 1: %u", dwSocket2);
    #else
    						if (pItemAward->dwSocket2 != 0)
    							dwSocket2 = pItemAward->dwSocket2;
    						else
    							dwSocket2 = pItemTable->alValues[0];
    #endif
    					}

    And this is the output:

    SYSERR: Jan 14 20:31:34 :: RESULT_SAFEBOX_LOAD: Socket2 0: 28435
    SYSERR: Jan 14 20:31:34 :: RESULT_SAFEBOX_LOAD: bType: 16
    SYSERR: Jan 14 20:31:34 :: RESULT_SAFEBOX_LOAD: bSubType: 2
    SYSERR: Jan 14 20:31:34 :: RESULT_SAFEBOX_LOAD: Socket2 1: 180
    SYSERR: Jan 14 20:31:34 :: RESULT_SAFEBOX_LOAD: Query: INSERT INTO item (id, owner_id, item.window, pos, vnum, count, socket0, socket1, socket2, attrtype0, attrvalue0, attrtype1, attrvalue1, attrtype2, attrvalue2, attrtype3, attrvalue3, attrtype4, attrvalue4, attrtype5, attrvalue5, attrtype6, attrvalue6) VALUES(2000000012, 1, 'MALL', 1, 61096, 1, 28433, 28434, 180, 72, 65, 71, 30, 5, 12, 9, 20, 12, 8, 0, 0, 0, 0)

    This is the proto of that item:

    61096	LaminaXPTO	ITEM_WEAPON	WEAPON_TWO_HANDED	3	ANTI_ASSASSIN | ANTI_SURA | ANTI_MUDANG | ANTI_DROP | ANTI_SELL	ITEM_TUNABLE	WEAR_WEAPON	NONE	0	0	0	0	15	LEVEL	240	LIMIT_NONE	0	APPLY_ATT_SPEED	65	APPLY_ATTBONUS_HUMAN	55	APPLY_CRITICAL_PCT	50	0	550	650	600	700	70	0	3	-1															

    This is what I used to create it:

    INSERT INTO `player`.`item_award` (`login`, `vnum`, `count`, `socket0`, `socket1`, `socket2`, `attrtype0`, `attrvalue0`, `attrtype1`, `attrvalue1`, `attrtype2`, `attrvalue2`, `attrtype3`, `attrvalue3`, `attrtype4`, `attrvalue4`,  `mall`)
    VALUES (
    	'admin',	-- LOGIN
    	61096,		-- ITEM ID
    	1,		-- COUNT
    	28433, 28434, 28435,	-- SLOTS
    	72, 65,		-- DM
    	71, 30,		-- DH,
    	5, 12,		-- APPLY_STR
    	9, 20,		-- APPLY_CAST_SPEED
    	12, 8,		-- APPLY_POISON_PCT
    	1		-- LOCATION
    );

    This is the result on item_award:

    id	pid	login	vnum	count	given_time	taken_time	item_id	why	socket0	socket1	socket2	socket3	socket4	socket5	attrtype0	attrvalue0	attrtype1	attrvalue1	attrtype2	attrvalue2	attrtype3	attrvalue3	attrtype4	attrvalue4	attrtype5	attrvalue5	attrtype6	attrvalue6	mall
    19	0	admin	61096	1	1000-01-01 00:00:00				28433	28434	28435	0	0	0	72	65	71	30	5	12	9	20	12	8	0	0	0	0	1

    And this is what I get when I open the mall:

    id	owner_id	window	pos	count	vnum	transmutation	socket0	socket1	socket2	socket3	socket4	socket5	attrtype0	attrvalue0	attrtype1	attrvalue1	attrtype2	attrvalue2	attrtype3	attrvalue3	attrtype4	attrvalue4	attrtype5	attrvalue5	attrtype6	attrvalue6
    2000000012	1	MALL	1	1	61096	0	28433	28434	180	0	0	0	72	65	71	30	5	12	9	20	12	8	0	0	0	0

    The Socket2 is getting fucked up and I have no clue why. I have no idea how the item type is being assumed as ITEM_UNIQUE.

    Anyone has any idea on how to solve it?

    For some reason the same db works on my other server and this issue doesn't happen, the only difference is the mysql database.

    Thank you

    Related to my problem from above, and thanks to @ Abel(Tiger) for sending me this post by him: 

    This particular problem has been solved, but more issues arose.

    First, if I added 3 stones, the 1st and 2nd slot would be set as 1 and only the 3rd would have a stone, that issue has been fixed by changing void ItemAwardManager::CheckItemSocket function's body to this:

    void ItemAwardManager::CheckItemSocket(TItemAward & rkItemAward, const TItemTable & rkItemTable)
    {
        // check for limited time items
        auto hasTimeLimit = false;
        for (size_t i = 0 ; i < ITEM_LIMIT_MAX_NUM && !hasTimeLimit; i++)
        {
            if (LIMIT_REAL_TIME == rkItemTable.aLimits[i].bType || LIMIT_REAL_TIME_START_FIRST_USE == rkItemTable.aLimits[i].bType)
                hasTimeLimit = true;
        }
    
        // check slottable sockets for non limited time items
        const auto maxSockets = std::min<int>(rkItemTable.bGainSocketPct, ITEM_SOCKET_MAX_NUM);
        if (maxSockets <= 0 || hasTimeLimit)
            return;
    
        //This if's have been changed to check if the socket* is 0, and only then set it to 1
        if (maxSockets >= 1 && rkItemAward.dwSocket0 == 0)
            rkItemAward.dwSocket0 = 1;
        if (maxSockets >= 2 && rkItemAward.dwSocket1 == 0)
            rkItemAward.dwSocket1 = 1;
        if (maxSockets >= 3 && rkItemAward.dwSocket2 == 0)
            rkItemAward.dwSocket2 = 1;
    }

    Then another problem presented itself, if I set socket2 as 0, it wouldn't be changed to 1 on the function above, because it would edit the value on pItemTable and not on the variable dwSocket2 that is used on the query. This issue was fixed by changing this:

    //Change the function call with this:
    ItemAwardManager::instance().CheckItemSocket(*pItemAward, *pItemTable, &dwSocket2);
    
    //Change the function header on ItemAwardManager.h with this:
    void				CheckItemSocket(TItemAward & pkItemAward, const TItemTable & pkItemTable, DWORD *dwSocket2);
    
    //Change the function body with this:
    void ItemAwardManager::CheckItemSocket(TItemAward & rkItemAward, const TItemTable & rkItemTable, DWORD *dwSocket2)
    {
        // check for limited time items
        auto hasTimeLimit = false;
        for (size_t i = 0 ; i < ITEM_LIMIT_MAX_NUM && !hasTimeLimit; i++)
        {
            if (LIMIT_REAL_TIME == rkItemTable.aLimits[i].bType || LIMIT_REAL_TIME_START_FIRST_USE == rkItemTable.aLimits[i].bType)
                hasTimeLimit = true;
        }
    
        // check slottable sockets for non limited time items
        const auto maxSockets = std::min<int>(rkItemTable.bGainSocketPct, ITEM_SOCKET_MAX_NUM);
        if (maxSockets <= 0 || hasTimeLimit)
            return;
    
        if (maxSockets >= 1 && rkItemAward.dwSocket0 == 0)
            rkItemAward.dwSocket0 = 1;
        if (maxSockets >= 2 && rkItemAward.dwSocket1 == 0)
            rkItemAward.dwSocket1 = 1;
        if (maxSockets >= 3 && rkItemAward.dwSocket2 == 0) {
            rkItemAward.dwSocket2 = 1;
            *dwSocket2 = 1;
        }
    }

    Then another problem come up, ITEM_UNIQUE would always replace the value you set on socket2 by the default value of the item, and to fix this, just change:

    //This:
    if (pItemTable->bType == ITEM_UNIQUE)
    
    //With this:
    if (pItemTable->bType == ITEM_UNIQUE && pItemAward->dwSocket2 == 0)

    Hope this helps someone.

    • Metin2 Dev 13
    • Good 2
    • Love 3
  11. 6 minutes ago, 3bd0 said:

    Hi,

    Did anyone manage to use Cef (Chromium Embedded Framework) version 90 or newer with metin 2? While older versions work fine for me, newer version don't. When I open the browser the client just crash without any apparent reason.

    Thanks.

    With me it doesn't crash, but the window stays white for some reason...

  12. 3 minutes ago, Draveniou1 said:

    In all versions there are loaded codes that load unjustly in freebsd example USB / CAMERA and many more  it is in vain to unjustly load these systems

    omfg-are-you-serious.gif

    At this point I can't decide if you are just trolling or your are dumb as f***. You are talking about the drivers INSIDE THE KERNEL that's no system or software or none of that shit you have been talking about. If you want to remove this you just remove them from the fucking KERNEL and build it yourself. This doesn't affect performance at all.

    Go ahead and report me for "bothering" your.

    • Good 1
  13. 5 minutes ago, Draveniou1 said:

    FreeBSD 13 Will it be clean or will all have these useless codes as in 11.4 ?   If you notice FreeBSD loads close to 700 MB on a host medium these 700 MB ram is from bad code   Thanks

    Not sure what you are talking about, the screenshot I posted above is running on FreeBSD 13 and doesn't even reach 650MB of RAM and that's with 400MB of them being used by MySQL server...

  14. Update

    Added a slider component that can be enabled or disabled (enabled by default) (instructions on how to use it are available on the repository).

    fy5TRcz.png

     

    Changed from CRA (Create-React-App aka react-scripts) to Vite, which allows for a faster development, build time and reduced package size. Also replaced the compression method from "maximum" to "normal", since there is no difference in sizes, but takes much less time to build.

    sPkBkq6.png

    This is the hidden content, please

    • Metin2 Dev 27
    • Eyes 1
    • Think 1
    • Confused 1
    • Good 7
    • Love 1
    • Love 19
  15. 7 minutes ago, Draveniou1 said:

    Try to find freebsd without useless codes active
    And then you can tell me your time and your details in detail and you will notice big differences in speed your server and in game 🙂

    Bruh, you never tried it, but you are sure as fuck that it would work. I admire your confidence and persistence...

    Learn FreeBSD, do it yourself and then share the results.

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