Jump to content

Amun

Contributor
  • Posts

    199
  • Joined

  • Last visited

  • Days Won

    1
  • Feedback

    100%

Posts posted by Amun

  1. This is the hidden content, please

    Metin2 Download

     

    Hi

     

    I noticed quite a bit of people have problems opening a dev server on Tmp4's source.

    I moved the server source to windows hoping it'd be easier for any beginners to get started. If anyone wants to give it a try, here it is:

    This is the hidden content, please

    I've also removed matrix card, auction, udp port, etc.. Not all of them, but quite a few.

     

    NOTE: If you don't have the Client from Tmp4, go to his topic and download it(maybe give him a thanks while you're there).

    Here's the topic:

     

     

    OK, let's get started.

    How to:
     

    Download the files and move them in a folder.

    Open a terminal (CMD/PowerShell/GitBash, etc..) and cd into the folder where you downloaded the files.

    Now run:

    tar -xvzf client_src.tgz
    tar -xvzf root.tgz
    tar -xvzf server_extern.tgz
    tar -xvzf server_src.tgz
    tar -xvzf winsf.tgz

    If you get any errors from tar, you might need to install Git Bash(or just use WinRar or something).

     

    I wanted to give you the server already compiled, just to get started faster, but I cleaned up the objects and forgot to make a backup of the game and db.exe..

    So, you'll have to compile them yourself, unfortunately.

     

    Go to server_src and open m2_server.2008.sln(I didn't rename it, do it yourself) then select Debug(or release, whatever you want) and Win32.

    After the game and db files are compiled, go back and open `winsf` folder.

    Now run `make_test_env.bat` with admin rights(it needs them to create the shortcuts(links)).

    After each compilation you just run `refresh.bat` to remove the old exe files and replace them with the new ones(Or just change the out directory in VS to winsf.. your choice).

     

    To start in debug mode, just run `run_d.bat`.

     

    If you don't have mysql server installed on your PC, get it from here:

    https://downloads.mysql.com/archives/installer/?version=5.6.26

    And click the second button(294.4MB).

     

    After it's installed, connect to the mysql server with either MySql WorkBench or with Navicat and import the sql scripts found in sql.rar.

    The tables are taken from some old serverfiles(from Marty, if I'm not mistaken). The tables are fucked, but it should work fine to get started.

    I'll fix them up later if I have time.. but not now.. Also, don't forget to change the user and password in the configs.(You'll find them in share/cores_config).

     

    Also. If your PC slows down while compiling, you might want to disable Multi-processor Compilation(I don't know if it's user specific or if it's saved in the solution's config, but I wanted to mention it just in case it's saved).

     

    The last step is to copy the exe files from client_src/binary into your client, pack the root folder with Eternexus(or whatever you use) and replace them in Client/pack.

    Here's two pictures:

     

    If I forgot anything, just let me know and I'll update the post as soon as I can. Also, please report whatever bugs/errors you find. It would be greatly appreciated.

    Cheers!

     

    Virus total found BitDefenderTheta in server_extern, but Antivirus found none, so you decide if you want to use them or just compile the libs yourself:

    Spoiler

    root

    This is the hidden content, please

    server_src

    This is the hidden content, please

    sql

    This is the hidden content, please

    winsf

    This is the hidden content, please

    client_src

    This is the hidden content, please

    server_extern

    This is the hidden content, please

     

    Spoiler

    Spoiler

    • Metin2 Dev 165
    • kekw 2
    • Eyes 5
    • Dislove 1
    • Angry 1
    • Cry 1
    • Smile Tear 1
    • Think 2
    • Scream 1
    • Lmao 2
    • Good 37
    • Love 10
    • Love 99
  2. If the db doesn't start, then nothing will, so open ClientManagerBoot and check what tables should be loaded from mysql at boot time(if you don't have logs, add logs as well so you can see what's loaded and what isn't).

     

    There's a lot of stuff that can cause the db/cores to shut down without any syserr but let's start with that and we'll see what's to be done later.

     

    Also, you can find me on discord at: Amun#3808

  3. Open char_skill.cpp and look for 

    bool CHARACTER::LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb)

     

    Replace the whole function with:

    Spoiler
    
    bool CHARACTER::LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb)
    {
    	const CSkillProto *pkSk = CSkillManager::instance().Get(dwSkillVnum);
    
    	if (!pkSk)
    		return false;
    
    	if (!IsLearnableSkill(dwSkillVnum))
    	{
    		ChatPacket(CHAT_TYPE_INFO, LC_TEXT("수련할 수 없는 스킬입니다."));
    		return false;
    	}
    
    	DWORD need_exp = 0;
    
    	if (FN_should_check_exp(this))
    	{
    		need_exp = 20000;
    
    		if (GetExp() < need_exp)
    		{
    			ChatPacket(CHAT_TYPE_INFO, LC_TEXT("경험치가 부족하여 책을 읽을 수 없습니다."));
    			return false;
    		}
    	}
    
    	if (pkSk->dwType != 0)
    	{
    		if (GetSkillMasterType(dwSkillVnum) != SKILL_MASTER)
    		{
    			if (GetSkillMasterType(dwSkillVnum) > SKILL_MASTER)
    				ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 스킬은 책으로 더이상 수련할 수 없습니다."));
    			else
    				ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 스킬은 아직 책으로 수련할 경지에 이르지 않았습니다."));
    			return false;
    		}
    	}
    
    	if (get_global_time() < GetSkillNextReadTime(dwSkillVnum))
    	{
    		if (!(test_server && quest::CQuestManager::instance().GetEventFlag("no_read_delay")))
    		{
    			if (FindAffect(AFFECT_SKILL_NO_BOOK_DELAY))
    			{
    				RemoveAffect(AFFECT_SKILL_NO_BOOK_DELAY);
    				ChatPacket(CHAT_TYPE_INFO, LC_TEXT("주안술서를 통해 주화입마에서 빠져나왔습니다."));
    			}
    			else
    			{
    				SkillLearnWaitMoreTimeMessage(static_cast<DWORD>(GetSkillNextReadTime(dwSkillVnum) - get_global_time()));
    				return false;
    			}
    		}
    	}
    
    	BYTE bLastLevel = GetSkillLevel(dwSkillVnum);
    
    	if (bProb != 0)
    	{
    		if (FindAffect(AFFECT_SKILL_BOOK_BONUS))
    		{
    			bProb += bProb / 2;
    			RemoveAffect(AFFECT_SKILL_BOOK_BONUS);
    		}
    		sys_log(0, "LearnSkillByBook Pct %u prob %d", dwSkillVnum, bProb);
    
    		if (number(1, 100) <= bProb)
    		{
    			if (test_server)
    				sys_log(0, "LearnSkillByBook %u SUCC", dwSkillVnum);
    
    			SkillLevelUp(dwSkillVnum, SKILL_UP_BY_BOOK);
    		}
    		else
    		{
    			if (test_server)
    				sys_log(0, "LearnSkillByBook %u FAIL", dwSkillVnum);
    		}
    	}
    	else
    	{
    		int idx = MIN(9, GetSkillLevel(dwSkillVnum) - 20);
    
    		sys_log(0, "LearnSkillByBook %s table idx %d value %d", GetName(), idx, aiSkillBookCountForLevelUp[idx]);
    
    		{
    			int need_bookcount = GetSkillLevel(dwSkillVnum) - 20;
    
    			PointChange(POINT_EXP, -static_cast<int>(need_exp));
    
    			quest::CQuestManager &q = quest::CQuestManager::instance();
    			quest::PC *pPC = q.GetPC(GetPlayerID());
    
    			if (pPC)
    			{
    				char flag[128 + 1];
    				memset(flag, 0, sizeof(flag));
    				snprintf(flag, sizeof(flag), "traning_master_skill.%u.read_count", dwSkillVnum);
    
    				int read_count = pPC->GetFlag(flag);
    				int percent = 65;
    
    				if (FindAffect(AFFECT_SKILL_BOOK_BONUS))
    				{
    					percent = 0;
    					RemoveAffect(AFFECT_SKILL_BOOK_BONUS);
    				}
    
    				if (number(1, 100) > percent)
    				{
    					if (read_count >= need_bookcount)
    					{
    						SkillLevelUp(dwSkillVnum, SKILL_UP_BY_BOOK);
    						pPC->SetFlag(flag, 0);
    
    						ChatPacket(CHAT_TYPE_INFO, LC_TEXT("책으로 더 높은 경지의 수련을 성공적으로 끝내셨습니다."));
    						LogManager::instance().CharLog(this, dwSkillVnum, "READ_SUCCESS", "");
    						return true;
    					}
    					else
    					{
    						pPC->SetFlag(flag, read_count + 1);
    
    						switch (number(1, 3))
    						{
    							case 1:
    								ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("어느정도 이 기술에 대해 이해가 되었지만 조금 부족한듯 한데.."));
    								break;
    
    							case 2:
    								ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("드디어 끝이 보이는 건가... 이 기술은 이해하기가 너무 힘들어.."));
    								break;
    
    							case 3:
    							default:
    								ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("열심히 하는 배움을 가지는 것만이 기술을 배울수 있는 유일한 길이다.."));
    								break;
    						}
    
    						ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d 권을 더 읽어야 수련을 완료 할 수 있습니다."), need_bookcount - read_count);
    						return true;
    					}
    				}
    			}
    			else
    			{
    			}
    		}
    	}
    
    	if (bLastLevel != GetSkillLevel(dwSkillVnum))
    	{
    		ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("몸에서 뭔가 힘이 터져 나오는 기분이야!"));
    		ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("뜨거운 무엇이 계속 용솟음치고 있어! 이건, 이것은!"));
    		ChatPacket(CHAT_TYPE_INFO, LC_TEXT("책으로 더 높은 경지의 수련을 성공적으로 끝내셨습니다."));
    		LogManager::instance().CharLog(this, dwSkillVnum, "READ_SUCCESS", "");
    	}
    	else
    	{
    		ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("크윽, 기가 역류하고 있어! 이거 설마 주화입마인가!? 젠장!"));
    		ChatPacket(CHAT_TYPE_INFO, LC_TEXT("수련이 실패로 끝났습니다. 다시 도전해주시기 바랍니다."));
    		LogManager::instance().CharLog(this, dwSkillVnum, "READ_FAIL", "");
    	}
    
    	return true;
    }

     

     

    Good Luck

     

    If you have problems, discord: Amun#3808

    • Love 1
  4. Hi mate,

     

    I've never used the system but I found an image trying to find it, so open ui.py and look for something like:

    wndMgr.SetSlotLevelImage

     

    If you still can't find it, let me know on Discord: Amun#3808 and I'll remove it for you..

     

    Good Luck !

     

    Edit: I forgot to tell you to also follow the code into your source(should be in eterPythonLib/PythonWIndowManagerModule.cpp) and get rid of the function from there as well. It doesn't affect keeping the function there but what's the point if you never use it, right ?

  5. Client source, ActorInstanceCollisionDetection.cpp

    Search for: 

    BOOL CActorInstance::TestActorCollision(CActorInstance& rVictim)

    Write your conditions inside that function.

     

    Example: 

    Disable collision with pets:

    	if (rVictim.GetRace() >= 34001 && rVictim.GetRace() <= 34099)
    		return FALSE;

     

    To disable collision with stones, just add another condition with the vnum range of the stones or individually if you only want it disabled for specific stones. NOTE: You should add a check to see if the player is attacking or not. I have never disabled the collision with metin stones so I am unsure if you'll be able to attack them anymore without a check.

     

    Untested Example for all metin stones(at least I think that's all of them):

    	if (!isAttacking() && rVictim.GetRace() >= 8001 && rVictim.GetRace() <= 8206)
    		return false;

     

    I advise you against disabling collision with houses and other objects since you'll just go right through them so they become useless.

     

    That can be done from __TestObjectCollision

     

    If you have any questions, let me know here or on Discord: Amun#3808

    Good luck !

  6. I don't get it.. mine's working just fine.

    Here's the code, I've also commented to see what's what:

    char_change_empire.cpp

    Spoiler

    int CHARACTER::ChangeEmpire(BYTE empire)
    {
        if (GetEmpire() == empire)
            return 1;//return 1 means you're trying to change to the same empire. Aka: you're already there..

        char szQuery[1024 + 1];
        DWORD dwAID;
        DWORD dwPID[4];
        memset(dwPID, 0, sizeof(dwPID));

        {//get all the players on this account
            snprintf(szQuery, sizeof(szQuery),        //pid5 after pid4 if you have lykan                            //pid5=%u if you have lykan
                     "SELECT id, pid1, pid2, pid3, pid4 FROM player_index%s WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u",
                                         //add one more GetPlayerID() if you have lykan
                     get_table_postfix(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire());

            std::unique_ptr<SQLMsg> msg(DBManager::instance().DirectQuery(szQuery));

            if (msg->Get()->uiNumRows == 0)
            {
                return 0;//return 0 is unknown or query error
            }

            MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult);

            str_to_number(dwAID, row[0]);
            str_to_number(dwPID[0], row[1]);
            str_to_number(dwPID[1], row[2]);
            str_to_number(dwPID[2], row[3]);
            str_to_number(dwPID[3], row[4]);
            // str_to_number(dwPID[4], row[5]);//if you have lykan.
        }

        const int loop = 4;//number of characters available(should be 5 if you have lykan)

        {
            DWORD dwGuildID[4];
            CGuild *pGuild[4];
            SQLMsg *pMsg = NULL;

            for (int i = 0; i < loop; ++i)
            {

                snprintf(szQuery, sizeof(szQuery), "SELECT guild_id FROM guild_member%s WHERE pid=%u", get_table_postfix(), dwPID[i]);

                pMsg = DBManager::instance().DirectQuery(szQuery);

                
                if (pMsg != NULL)//query failed
                {
                    if (pMsg->Get()->uiNumRows > 0)//query didn't fail, we've got some results.
                    {
                        MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
                        str_to_number(dwGuildID[i], row[0]);

                        pGuild[i] = CGuildManager::instance().FindGuild(dwGuildID[i]);//look for a guild

                        if (pGuild[i] != NULL)//we found a guild on one of the accounts
                        {
                            M2_DELETE(pMsg);
                            return 2;
                        }
                    }
                    else // we didn't get any results
                    {
                        dwGuildID[i] = 0;
                        pGuild[i] = NULL;
                    }
                    M2_DELETE(pMsg);
                }
            }
        }

        {//return 3 means one of the characters is married.
            for (int i = 0; i < loop; ++i)
            {
                if (marriage::CManager::instance().IsEngagedOrMarried(dwPID[i]) == true)
                    return 3;
            }
        }

        {
            //don't forget to add your 5th pid if you have lykan.
            snprintf(szQuery, sizeof(szQuery), "UPDATE player_index%s SET empire=%u WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u",
                     get_table_postfix(), empire, GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire());

            std::unique_ptr<SQLMsg> msg(DBManager::instance().DirectQuery(szQuery));

            if (msg->Get()->uiAffectedRows > 0)
            {
                SetChangeEmpireCount();
                SetEmpire(empire);
                UpdatePacket();
                return 999;//it means everything ran as expected.
            }
        }

        return 0;//unexpectedly, we've reached the final destination. How did this happen ?
    }

    This should do the trick.

     

    Good luck !

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