Jump to content

Recommended Posts

Hi guys, I need your help!
I compile metin2 using visualstudio (server side) and I have a problem with the drop.
As you can see, the drop is set to 1% but despite this I continue to drop, at each mob 101 eliminated, at least 2 of the items in the list.
On freebsd it didn't happen.
Some advice?

 

Group test_dog
{
	type	drop
	mob	101
	1	1012	1	1
	2	4012	1	1
	3	11402	1	1
	4	16022	1	1
}

 

Link to comment
Share on other sites

UP2:

 

if (iPercent >= number(1, iRandRange))

 

iPercent = 100000                      | in mob_drop_item the percentage is set to 10

number = 1, 4000000                | random number between 1 and 4000000

 

I noticed, however, how the random values obtained are very different between the two versions:

 

random = 3894439
random = 229634
random = 1664283
random = 3958197 | BSD VERSION
random = 924476
random = 2193081
random = 181074
-----------------------------------------------------------------------------

random = 12851
random = 10165 | WINDOWS VERSION
random = 23397
random = 12871

I don't enter all the other values because they follow the same trend.
In the first case, having a high random value I will not drop, while having a low one in the second case I will always drop.
Any ideas on this?

Link to comment
Share on other sites

  • 1 year later...
On 5/3/2020 at 12:08 PM, Hik said:

UP2:

 

if (iPercent >= number(1, iRandRange))

 

iPercent = 100000                      | in mob_drop_item the percentage is set to 10

number = 1, 4000000                | random number between 1 and 4000000

 

I noticed, however, how the random values obtained are very different between the two versions:

 

random = 3894439
random = 229634
random = 1664283
random = 3958197 | BSD VERSION
random = 924476
random = 2193081
random = 181074
-----------------------------------------------------------------------------

random = 12851
random = 10165 | WINDOWS VERSION
random = 23397
random = 12871

I don't enter all the other values because they follow the same trend.
In the first case, having a high random value I will not drop, while having a low one in the second case I will always drop.
Any ideas on this?

Hi Hik, your searching was useful to me, as you said every windows server file is affected by this bug.

I found the root cause. 

File: utils.c in libthecore

DWORD thecore_random()
{
#ifdef __WIN32__
    return rand();
#else
    return random();
#endif

 

for Windows, it will use rand(). It has limitations for Windows, the maximum range is 32767. That's why the number extraction in Windows is so low.

You have to find another way to random with bigger numbers, or as a workaround you can multiply the rand() value to have a maximum of iRandRange.

You can find the multiplier value by dividing iRandRange / 32767.

It's working, but it's a really bad workaround if you tell me, because we are not get a random result from 1 to iRandRange. 

 

If you want to try this workaround, find item_manager.cpp and change:

 

number(1,iRandRange)

To

(rand() % 32767) * iMultiplier)

 

Before 

while (it != g_vec_pkCommonDropItem[bRank].end()) in CreateDropItem

add this:

int iMultiplier = iRandRange / 32767

 

Before 

while sys_log(1, "CreateQuestDropItem victim(%s), killer(%s)", pkChr->GetName(), pkKiller->GetName() ); in CreateDropItem

add this:

int iMultiplier = iRandRange / 32767

 

My advise is to work in a good implementation of random for Windows.

Cheers

  • Good 1
Link to comment
Share on other sites

  • Developer
On 3/14/2022 at 10:38 PM, Hik said:

In the end i used this:

 

int random_drop(int min, int max)
{
    std::random_device device;
    std::mt19937 generator(device());
    std::uniform_int_distribution<int> distribution(min, max);
    return distribution(generator);
}

 

It works yeah, but it is really slow.

I would suggest you something much faster.

int number(int v1, int v2) 
{
	thread_local std::default_random_engine _Generator;
	thread_local bool _Init = false;

	if (!_Init)
	{
		_Init = true;
		std::random_device dev;
		_Generator.seed(dev());
	}

	std::uniform_int_distribution<int> distribution(v1, v2);
	return distribution(_Generator);
}

It can totally replace the old "number" function from libthrecore (you have to remove the macro defined inside libthrecore/inlude/utils.h)
 

Here's a small test showing how my function is faster:
https://onlinegdb.com/27jNGeGOlx
 

Execution time in seconds (1.000.000 function calls):

number time : 0.0718687
random_drop time : 17.9117

 

 

Edited by Ikarus_
  • Good 2
  • Love 1
  • Love 1

My youtube channel  on which you can see my works here

Link to comment
Share on other sites

  • 1 year later...
  • Active Member

You have to create (and seed) the generator outside your function, don't want a new random engine every time. That way both of your codes do (near) the same in the same amount of time.

number time : 0.0781763
random_drop time : 0.0759912

https://onlinegdb.com/6P2Rb7K9S

 

Btw thanks for the idea, i've choosen a more simple way.

libthecore/utilc.c:

DWORD thecore_random()
{
#ifdef _WIN32
//    return rand(); //extend the 32767 limit to 2147483645...
    return rand() * rand() + rand();
#else
    return random();
#endif
}

Not faster and not better, but it works 🙂

Link to comment
Share on other sites

  • Developer
12 minutes ago, ATAG said:

You have to create (and seed) the generator outside your function, don't want a new random engine every time. That way both of your codes do (near) the same in the same amount of time.

number time : 0.0781763
random_drop time : 0.0759912

https://onlinegdb.com/6P2Rb7K9S

 

Btw thanks for the idea, i've choosen a more simple way.

libthecore/utilc.c:

DWORD thecore_random()
{
#ifdef _WIN32
//    return rand(); //extend the 32767 limit to 2147483645...
    return rand() * rand() + rand();
#else
    return random();
#endif
}

Not faster and not better, but it works 🙂

That way u do isn't a uniform distribution. If u want see it with ur eyes try to store 1milion numbers returned by ur functions and print them sorting by count of times they were picked. Larger numbers are more frequently picked than smaller ones.

Edited by Ikarus_

My youtube channel  on which you can see my works here

Link to comment
Share on other sites

  • Premium
3 hours ago, ATAG said:

In our case this is not a problem, higher number means less drop 😄

Yeah, let's make 2 kilos of salad with 5% lettuce, 10% tomatoes, 40% pine nuts, 30% olive oil, 15% balsamic vinegar, in the end it's a lot of food, so I'm sure it'll be good!.. right?

Edited by Syreldar
  • kekw 2

 

"Nothing's free in this life.

Ignorant people have an obligation to make up for their ignorance by paying those who help them.

Either you got the brains or cash, if you lack both you're useless."

Syreldar

Link to comment
Share on other sites

  • Developer
6 hours ago, ATAG said:

Final version, This way the drops are near the same (and fast enough)

DWORD thecore_random()
{
#ifdef _WIN32
    double iRand = (double)rand() / RAND_MAX; // 0x7fff
    return (iRand * (0x7fffffff - 1) + 1);
#else
    return random(); // 0x7fffffff
#endif
}

 

Again no xd.

It is still not uniform at all.

Using the same test (1 milion numbers) you will see that only 65k of numbers are possible to be picked... And 0 will never be picked xd

The possible numbers are  the ones that comes out using this formula :

n = 1 + x * 32K 

where x can be one random number from 0 to RAND_MAX (65k~)

in short, theres a number every 32k of numbers which can be picked, in the range from 1 to 2147483647, which is very far from being a uniform distribution in a range.

 

I guess u know that it cant be tested on onlinegdb since this issue comes out only on Windows (onlinegdb uses linux)

Edited by Ikarus_
  • Good 1

My youtube channel  on which you can see my works here

Link to comment
Share on other sites

  • Active Member

You are wrong.  Put this into vs->console app, and run it:

#include <iostream>
#include <random>
#include <limits>

int thecore_random()
{
    double iRand = (double)rand() / RAND_MAX; // 0x7fff
    return (int)(iRand * 0x7fffffff);
}

int main()
{
    std::cout << "RAND_MAX: " << RAND_MAX << "\n";
    std::cout << "INT_MAX: " << INT_MAX << "\n";
    for (int i=0; i<100; i++)
        std::cout << "random: " << thecore_random() << "\n";
}

spacer.png

The range was 1-RAND_MAX, changed it to 0... Thank you 🙂

Edited by Metin2 Dev International
Core X - External 2 Internal
Link to comment
Share on other sites

  • Developer
19 minutes ago, ATAG said:

You are wrong.  Put this into vs->console app, and run it:

#include <iostream>
#include <random>
#include <limits>

int thecore_random()
{
    double iRand = (double)rand() / RAND_MAX; // 0x7fff
    return (int)(iRand * 0x7fffffff);
}

int main()
{
    std::cout << "RAND_MAX: " << RAND_MAX << "\n";
    std::cout << "INT_MAX: " << INT_MAX << "\n";
    for (int i=0; i<100; i++)
        std::cout << "random: " << thecore_random() << "\n";
}

spacer.png

The range was 1-RAND_MAX, changed it to 0... Thank you 🙂

Store the values into a set, change the for limit to 1milion and call .size on the set at the end. Surprise! The size is around 32k, so it is repeating 1milion of times the same 32k of values. So in short the problem i tried to explain in the previous answer consists in the fact the values are discrete. Are only 32k (RAND_MAX) of values, distributed in the range 0-2kkk

I said 65k of values in the previous post because I thought RAND_MAX was 65k, since it is 32k the numbers are more discrete than how i thought xd

Edited by Metin2 Dev International
Core X - External 2 Internal

My youtube channel  on which you can see my works here

Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

Announcements



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