proposal: gain XP per distance traveled #4956

Open
opened 2023-07-18 01:17:26 +00:00 by flux · 15 comments
Member

this would be a small amount, like 1 XP per 200 nodes. travel when in a minecart or otherwise "attached" would not count. teleportation will not count. we can make use of the fact that player:get_velocity() is zero if a player is moving while attached to a horse or minecart, or if they teleport. instead of measuring the distance the player moved between steps, we can scale their horizontal speed to the time elapsed and "guess" how far they've moved via normal motion.

i think someone proposed this idea before once, but i can't find an issue about it.

this would be a small amount, like 1 XP per 200 nodes. travel when in a minecart or otherwise "attached" would not count. teleportation will not count. we can make use of the fact that `player:get_velocity()` is zero if a player is moving while attached to a horse or minecart, or if they teleport. instead of measuring the distance the player moved between steps, we can scale their horizontal speed to the time elapsed and "guess" how far they've moved via normal motion. i think someone proposed this idea before once, but i can't find an issue about it.
flux added the
1. kind/enhancement
1. kind/balancing
labels 2023-07-18 01:17:26 +00:00

There still may be ways to abuse this, I can totally see people setting their homepoint at the top of a jumpshaft, then "fall down" for about 20 minutes and let some script or something enter t + / + h + enter. There is also the autoforwad feature, which could be used in long, straight tunnels. I assume "falling" and "autoforward" can be detected easily when the velocity (direction and speed) doesn't change much. The general idea is great and I'd like to make it happen.

What exactly do we want to support? Exploration and adventures. The question is, how do we detect that? Is it "covered horizontal distance"? Is it "velocity change"? Is it "be close to newly emerged mapblocks" ? What other discriminators could be used to detect exploration in contrast to cheating?

There still may be ways to abuse this, I can totally see people setting their homepoint at the top of a jumpshaft, then "fall down" for about 20 minutes and let some script or something enter `t + / + h + enter`. There is also the autoforwad feature, which could be used in long, straight tunnels. I assume "falling" and "autoforward" can be detected easily when the velocity (direction and speed) doesn't change much. The general idea is great and I'd like to make it happen. What exactly do we want to support? Exploration and adventures. The question is, how do we detect that? Is it "covered horizontal distance"? Is it "velocity change"? Is it "be close to newly emerged mapblocks" ? What other discriminators could be used to detect exploration in contrast to cheating?

If you gain xp by just walking thats the easiest thing to cheat with macro software
←↑→↓ repeat

In general I´m not against the idea but thats something that should be done at the start of a new server and not years later or is there any plan for players that explored alot before?

If you gain xp by just walking thats the easiest thing to cheat with macro software ←↑→↓ *repeat* In general I´m not against the idea but thats something that should be done at the start of a new server and not years later or is there any plan for players that explored alot before?
Member

Rewarding "dumb" actions will always be exploited and automated.

But maybe visiting a city for the first time should give xp?

Anyway, I think it's better to reward exploration by giving people more reasons to explore - generated structures with loot, "random encounters"?, or, for populated areas: landmarks, quests, reward chests etc.

Rewarding "dumb" actions will always be exploited and automated. But maybe visiting a city for the first time should give xp? Anyway, I think it's better to reward exploration by giving people more reasons to explore - generated structures with loot, "random encounters"?, or, for populated areas: landmarks, quests, reward chests etc.

XP for simple movement may be abusable with some mechanics, but you can actually reward exploration.

You will divide YL into sectors (1 sector = 100 x 100 in XZ, unlimited in Y), so the world would consists of approx 600 x 600 sectors.
Then you track number of visited sectors within a time window (say 10 minutes). For somebody just moving on spot or falling in hellevator, the number will be low. For people actually exploring new different lands, it will be high (especially if you sprint with crystal boots :) ). At end of time window, you reward XP and clear the internal list. The XP rewards could be non-linear based on number of visited sectors.

Teleporting long distances won't affect this much, as you land on new sector (+1) and then you have to walk some distance to increase the count again. Teleporting back and forth will not help at all, as only unique sectors are counted.

(actual value of constants could vary)

This perhaps is not 100% abuse-proof, but is IMHO more robust than other proposals.

XP for simple movement may be abusable with some mechanics, but you can actually reward exploration. You will divide YL into sectors (1 sector = 100 x 100 in XZ, unlimited in Y), so the world would consists of approx 600 x 600 sectors. Then you track number of visited sectors within a time window (say 10 minutes). For somebody just moving on spot or falling in hellevator, the number will be low. For people actually exploring new different lands, it will be high (especially if you sprint with crystal boots :) ). At end of time window, you reward XP and clear the internal list. The XP rewards could be non-linear based on number of visited sectors. Teleporting long distances won't affect this much, as you land on new sector (+1) and then you have to walk some distance to increase the count again. Teleporting back and forth will not help at all, as only unique sectors are counted. (actual value of constants could vary) This perhaps is not 100% abuse-proof, but is IMHO more robust than other proposals.
Author
Member

i like sixer's proposal, mostly. in my mind, we break the world into regions the size of a mapchunk (80 nodes, instead of 100), except they extend over the full y coordinates. give a player 1 xp every time they visit some point in a region they've never visited.

this would end up giving a maximum of 597529 points in total if a player visited all the places, which feels reasonably to me. a non-werewolf player sprinting and wearing crystal boots can traverse a mapblock in at best 1.3333 seconds. if you cover the map by going from x,z = MIN, MIN to MAX, MAX in a pattern like this:

>v>v^
^v^v^
^v^v^
^v^v^
^>^>^

it'll take a player over 1100 hours to visit all mapchunks, at best (there could be a more optimal path, but i doubt one that'd be much more optimal, maybe 1/2 of that value). but most importantly, if we consider ways of automating XP generation, this would be less exploitable than placing/breaking nodes automatically.

the major stopping issue here is in storing the relevant information. if we had access to a bitmap, we'd only need about 73KiB per player, and using compression could probably reduce that significantly (maybe 1.5KiB per player).

but lua doesn't provide access to a bitmap w/out installing a module. loading that module would require an insecure environment. so, the stopper for implementing such a feature is how willing alias is to allow access to an appropriate module.

i like sixer's proposal, mostly. in my mind, we break the world into regions the size of a mapchunk (80 nodes, instead of 100), except they extend over the full y coordinates. give a player 1 xp every time they visit some point in a region they've never visited. this would end up giving a maximum of 597529 points in total if a player visited all the places, which feels reasonably to me. a non-werewolf player sprinting and wearing crystal boots can traverse a mapblock in at best 1.3333 seconds. if you cover the map by going from x,z = MIN, MIN to MAX, MAX in a pattern like this: ``` >v>v^ ^v^v^ ^v^v^ ^v^v^ ^>^>^ ``` it'll take a player over 1100 hours to visit all mapchunks, at best (there could be a more optimal path, but i doubt one that'd be much more optimal, maybe 1/2 of that value). but most importantly, if we consider ways of automating XP generation, this would be less exploitable than placing/breaking nodes automatically. the major stopping issue here is in storing the relevant information. if we had access to a bitmap, we'd only need about 73KiB per player, and using compression could probably reduce that significantly (maybe 1.5KiB per player). but lua doesn't provide access to a bitmap w/out installing a module. loading that module would require an insecure environment. so, the stopper for implementing such a feature is how willing alias is to allow access to an appropriate module.

You could have different internal representation based on number of blocks in it. i.e. a table when it has less than 500 blocks and bitmap once it has more.

If there is problem with lua capabilities, you can just modify the c++ code of the server and put the tracking there (and expose via lua api). Or use a different representation (array of integers? string?) to store the data

You could have different internal representation based on number of blocks in it. i.e. a table when it has less than 500 blocks and bitmap once it has more. If there is problem with lua capabilities, you can just modify the c++ code of the server and put the tracking there (and expose via lua api). Or use a different representation (array of integers? string?) to store the data

Alias is entirely against using insecure env, but has no issues with http env. Storing n:m values on a player has been a qestuion with whosit as well and will need to be solved at the latest for the quest API. Slow n:m access could be done via webservice and http env, but

A more optimal path is walking along a chunk border and switching over every now and then. That should cut the time in 2, coz one can cover 2 stripe of chunks in one go.

Alias is entirely against using insecure env, but has no issues with http env. Storing n:m values on a player has been a qestuion with whosit as well and will need to be solved at the latest for the quest API. Slow n:m access could be done via webservice and http env, but A more optimal path is walking along a chunk border and switching over every now and then. That should cut the time in 2, coz one can cover 2 stripe of chunks in one go.
Author
Member

597529 points

storing this as a regular lua table w/ integer indices and true/false values would take about 16MiB per player. w/ 52 active players, that'd add up to 832 MiB, which is perhaps a bit too much.

however, when storing the data, compression would give us pretty decent results - it'd only require about 284KiB per player, and only about 400 bytes if the player hasn't traveled anywhere.

> 597529 points storing this as a regular lua table w/ integer indices and `true`/`false` values would take about 16MiB per player. w/ 52 active players, that'd add up to 832 MiB, which is perhaps a bit too much. however, when storing the data, compression would give us pretty decent results - it'd only require about 284KiB per player, and only about 400 *bytes* if the player hasn't traveled anywhere.

Slow n:m access could be done via webservice and http env, but

What would happen if the webservice would get down/crash while server is running?
I'd be careful with separating important core stuff to other services.

597529 points

storing this as a regular lua table w/ integer indices and true/false values would take about 16MiB per player. w/ 52 active players, that'd add up to 832 MiB, which is perhaps a bit too much.

however, when storing the data, compression would give us pretty decent results - it'd only require about 284KiB per player, and only about 400 bytes if the player hasn't traveled anywhere.

With compression, you have to decompress and compress on any update.

For "full" representation, you can use int to represent 32 bits (in 32 consecutive blocks) to store visited/unvisited state of 32 blocks - 597529 points would be 18673 int values and 74692 bytes - and reading/writing visited state of arbitrary block is fast

Or you can start with table with integer indices and once that grows too large (i.e. memory size close to the full array), you switch the representation to full array as suggested above.

Or, if the tables would be cleared periodically (like once per day/week/month), we can rely on them being usually reasonably small.

This would then reward re-exploration after that certain time, but I don't think that would be a big problem

> Slow n:m access could be done via webservice and http env, but What would happen if the webservice would get down/crash while server is running? I'd be careful with separating important core stuff to other services. > > 597529 points > > storing this as a regular lua table w/ integer indices and `true`/`false` values would take about 16MiB per player. w/ 52 active players, that'd add up to 832 MiB, which is perhaps a bit too much. > > however, when storing the data, compression would give us pretty decent results - it'd only require about 284KiB per player, and only about 400 *bytes* if the player hasn't traveled anywhere. With compression, you have to decompress and compress on any update. For "full" representation, you can use int to represent 32 bits (in 32 consecutive blocks) to store visited/unvisited state of 32 blocks - 597529 points would be 18673 int values and 74692 bytes - and reading/writing visited state of arbitrary block is fast Or you can start with table with integer indices and once that grows too large (i.e. memory size close to the full array), you switch the representation to full array as suggested above. Or, if the tables would be cleared periodically (like once per day/week/month), we can rely on them being usually reasonably small. This would then reward re-exploration after that certain time, but I don't think that would be a big problem

Slow n:m access could be done via webservice and http env, but

What would happen if the webservice would get down/crash while server is running?

In this case it would return something different than OK 200, and MT would have to try again.

I'd be careful with separating important core stuff to other services.

To the contrary, the less MT has to do, the better. I try to get rid of terrible Minetest-isms and have as much data in independent systems as possible. Still webservice is much slower than something internal, even though the webserver ofc is on the same machine.

597529 points

storing this as a regular lua table w/ integer indices and true/false values would take about 16MiB per player. w/ 52 active players, that'd add up to 832 MiB, which is perhaps a bit too much.

however, when storing the data, compression would give us pretty decent results - it'd only require about 284KiB per player, and only about 400 bytes if the player hasn't traveled anywhere.

With currently 70k accounts that might become 19.5 GB at worst and 27 MB at best.

With compression, you have to decompress and compress on any update.

That depends on how many updates we expect. Using webserver to store stuff will certainly not work in a "several reads and writes per serverstep" scenario. At best it could be cached ingame after login and then stored at logout. Even files would be faster or modstorage.

For "full" representation, you can use int to represent 32 bits (in 32 consecutive blocks) to store visited/unvisited state of 32 blocks - 597529 points would be 18673 int values and 74692 bytes - and reading/writing visited state of arbitrary block is fast

Or you can start with table with integer indices and once that grows too large (i.e. memory size close to the full array), you switch the representation to full array as suggested above.

Or, if the tables would be cleared periodically (like once per day/week/month), we can rely on them being usually reasonably small.

We'll at least need to know upper limits and we also need to prepare for at least one crazy person to reach those limits.

This would then reward re-exploration after that certain time, but I don't think that would be a big problem

That is a good idea, but we can't simply ditch the whole data and call a new cycle. Additional to what was explored, we'd have to store when it was explored.

> > Slow n:m access could be done via webservice and http env, but > > What would happen if the webservice would get down/crash while server is running? In this case it would return something different than OK 200, and MT would have to try again. > I'd be careful with separating important core stuff to other services. To the contrary, the less MT has to do, the better. I try to get rid of terrible Minetest-isms and have as much data in independent systems as possible. Still webservice is much slower than something internal, even though the webserver ofc is on the same machine. > > > 597529 points > > > > storing this as a regular lua table w/ integer indices and `true`/`false` values would take about 16MiB per player. w/ 52 active players, that'd add up to 832 MiB, which is perhaps a bit too much. > > > > however, when storing the data, compression would give us pretty decent results - it'd only require about 284KiB per player, and only about 400 *bytes* if the player hasn't traveled anywhere. With currently 70k accounts that might become 19.5 GB at worst and 27 MB at best. > With compression, you have to decompress and compress on any update. That depends on how many updates we expect. Using webserver to store stuff will certainly not work in a "several reads and writes per serverstep" scenario. At best it could be cached ingame after login and then stored at logout. Even files would be faster or modstorage. > For "full" representation, you can use int to represent 32 bits (in 32 consecutive blocks) to store visited/unvisited state of 32 blocks - 597529 points would be 18673 int values and 74692 bytes - and reading/writing visited state of arbitrary block is fast > > Or you can start with table with integer indices and once that grows too large (i.e. memory size close to the full array), you switch the representation to full array as suggested above. > > Or, if the tables would be cleared periodically (like once per day/week/month), we can rely on them being usually reasonably small. We'll at least need to know upper limits and we also need to prepare for at least one crazy person to reach those limits. > This would then reward re-exploration after that certain time, but I don't think that would be a big problem That is a good idea, but we can't simply ditch the whole data and call a new cycle. Additional to what was explored, we'd have to store when it was explored.
Author
Member

i've now implemented a bitarray class: https://github.com/fluxionary/minetest-futil/blob/main/collections/bitarray.lua

for the relevant data size, this now only takes 256KiB per active player, and only 77KiB when serialized without compression.

note that this is slow compared to a similar structure backed by c/c++, but this shouldn't be a problem for our use case.

also, remember that we don't need to serialize and compress and save these every time they change - only when a player logs out, and perhaps every 10 minutes or something to protect against server crashes.

i've now implemented a bitarray class: https://github.com/fluxionary/minetest-futil/blob/main/collections/bitarray.lua for the relevant data size, this now only takes 256KiB per active player, and only 77KiB when serialized without compression. note that this is *slow* compared to a similar structure backed by c/c++, but this shouldn't be a problem for our use case. also, remember that we don't need to serialize and compress and save these every time they change - only when a player logs out, and perhaps every 10 minutes or something to protect against server crashes.

Still webservice is much slower than something internal, even though the webserver ofc is on the same machine.

you can use different protocol with less overhead than http.

With currently 70k accounts that might become 19.5 GB at worst and 27 MB at best.

what part of map is actually explored by someone at all? 100% or less? that would maybe give lower upper bound

That is a good idea, but we can't simply ditch the whole data and call a new cycle. Additional to what was explored, we'd have to store when it was explored.

storing time for keys and expiring them after some time needs non-trivial data structure. One that would likely be inefficient in lua. Maybe you need to select some reasonable approximation of original idea that can be calculated within reasonable time and is close enough in its effect

> Still webservice is much slower than something internal, even though the webserver ofc is on the same machine. you can use different protocol with less overhead than http. > With currently 70k accounts that might become 19.5 GB at worst and 27 MB at best. what part of map is actually explored by someone at all? 100% or less? that would maybe give lower upper bound > That is a good idea, but we can't simply ditch the whole data and call a new cycle. Additional to what was explored, we'd have to store when it was explored. storing time for keys and expiring them after some time needs non-trivial data structure. One that would likely be inefficient in lua. Maybe you need to select some reasonable approximation of original idea that can be calculated within reasonable time and is close enough in its effect
Author
Member

@sixer: sorry for shooting down all your ideas, i encourage you to continue to contribute.

horizontal

it's easy to detect and discount falling in any of this. whether or not climbing should be incorporated is an open question.

you can use different protocol with less overhead than http.

minetest provides an insecure environment, and an http environment. alias currently will not use the insecure environment (tangent: i aim to convince him otherwise at some point, but not for this), so there's no chance of using a different protocol.

what part of map is actually explored by someone at all? 100% or less? that would maybe give lower upper bound

i don't have exact statistics for your-land, but i'm guessing more than half (maybe 80%?) of "players" don't get past initial spawn. as a rough estimate based on nothing more than feelings, i estimate at most 10% of the players who get past that point remain to explore beyond 100 mapchunks. with compression, nearly all of the serialized exploration parameters will require a negligible amount of storage.

@sixer: sorry for shooting down all your ideas, i encourage you to continue to contribute. > horizontal it's easy to detect and discount falling in any of this. whether or not climbing should be incorporated is an open question. > you can use different protocol with less overhead than http. minetest provides an insecure environment, and an http environment. alias currently will not use the insecure environment (tangent: i aim to convince him otherwise at some point, but not for this), so there's no chance of using a different protocol. > what part of map is actually explored by someone at all? 100% or less? that would maybe give lower upper bound i don't have exact statistics for your-land, but i'm guessing more than half (maybe 80%?) of "players" don't get past initial spawn. as a rough estimate based on nothing more than feelings, i estimate at most 10% of the players who get past that point remain to explore beyond 100 mapchunks. with compression, nearly all of the serialized exploration parameters will require a negligible amount of storage.

well, with insecure env the scripts would have arbitrary read/write access everywhere (or limited only by unix file permissions of the user) and you do not want that unless you can isolate thoae pernissions to a single trusted mod and ensure it cannot be abused transitively, like via callbacks. but http env could be extended to allow a different protocol, either have general network-access env or another env for that protocol. of course, that would need some modification of c++ code

well, with insecure env the scripts would have arbitrary read/write access everywhere (or limited only by unix file permissions of the user) and you do not want that unless you can isolate thoae pernissions to a single trusted mod and ensure it cannot be abused transitively, like via callbacks. but http env could be extended to allow a different protocol, either have general network-access env or another env for that protocol. of course, that would need some modification of c++ code
Author
Member

well, with insecure env the scripts would have arbitrary read/write access everywhere (or limited only by unix file permissions of the user) and you do not want that unless you can isolate thoae pernissions to a single trusted mod and ensure it cannot be abused transitively, like via callbacks. but http env could be extended to allow a different protocol, either have general network-access env or another env for that protocol. of course, that would need some modification of c++ code

normal usage of the insecure environment is designed around not leaking it. it's not hard to use it safely. to break the trust, you'd have to do something like pass an insecure file handle to a public function. i'm not aware of any exploitable mods, and i've certainly tried to find exploits. there could be a bug somewhere that makes it unsafe to use at all, but i find that about as likely as there being a way to escape the sandbox w/out access to that environment. in particular, i don't really trust luajit. it's really close to the metal and there's apparently only person in the world who really understands its internals.

so far as i know, no one has tried to distribute a malicious minetest mod, outside of proof-of-concepts in bug reports. such a thing will almost certainly crop up at some point in the future, but it's really unlikely.

> well, with insecure env the scripts would have arbitrary read/write access everywhere (or limited only by unix file permissions of the user) and you do not want that unless you can isolate thoae pernissions to a single trusted mod and ensure it cannot be abused transitively, like via callbacks. but http env could be extended to allow a different protocol, either have general network-access env or another env for that protocol. of course, that would need some modification of c++ code normal usage of the insecure environment is designed around not leaking it. it's not hard to use it safely. to break the trust, you'd have to do something like pass an insecure file handle to a public function. i'm not aware of any exploitable mods, and i've certainly tried to find exploits. there could be a bug somewhere that makes it unsafe to use at all, but i find that about as likely as there being a way to escape the sandbox w/out access to that environment. in particular, i don't really trust luajit. it's really close to the metal and there's apparently only person in the world who really understands its internals. so far as i know, no one has tried to distribute a malicious minetest mod, outside of proof-of-concepts in bug reports. such a thing will almost certainly crop up at some point in the future, but it's really unlikely.
Sign in to join this conversation.
No Milestone
No project
No Assignees
5 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: your-land/bugtracker#4956
No description provided.