Civcraft / NameLayer

Do not open issues here; open them on the maintained fork @ DevotedMC
https://github.com/DevotedMC/NameLayer
BSD 3-Clause "New" or "Revised" License
4 stars 14 forks source link

Add culltime for unused groups #165

Closed Scuwr closed 8 years ago

Scuwr commented 8 years ago

With the map being so small compared to the amount of people it will support land shortage will likely become a problem. One of my biggest issues while playing Civcraft was needing to destroy very old and well reinforced buildings from players who hadn't been on in years.

So I think there should be a culltime maybe lasting for a couple months or so. If no group admins or moderators login within that time, the reinforcements will cull.

Maxopoly commented 8 years ago

We had that exact same idea in internal discussions.

Implementation wise we would want to keep a time stamp for each group and refresh whenever someone in that group with block permissions logs on. Then as often as specified decay all the reinforcements on any inactive group, this has to be persisted across restarts and work crossshard

Scuwr commented 8 years ago

I suppose the most the difficult part would be implementing the login event. Do you know where the best place to start looking into that as a possibility would be?

Maxopoly commented 8 years ago

Hmm just get all the groups, iterate and if the player had BLOCKS permission, update the time stamp

Scuwr commented 8 years ago

Sorry, I meant is there a specific plugin we are using to handle all login events? Or will a basic override of the login event suffice?

Maxopoly commented 8 years ago

You could just do it the lazy way and recheck on every normal login on any shard and then update the db based on that, it's not performance intensive so it doesnt really matter. Just use a normal listener and listen for playerjoinevent

Scuwr commented 8 years ago

Which permission type should it be limited to? MEMBERS, MODS, ADMINS, OWNER?

Maxopoly commented 8 years ago

Just check whether the player has the permission BLOCKS, thats independent from mod/admin and will still work if people change around their group permissions.

Scuwr commented 8 years ago

Alright, I've got all that done. However, I'm now trying to decide what would be the best way detect when a group has exceeded the culltime. I could check on every block break, but I feel this might be necessary. Perhaps we could check every time the server restarts? Do you have any ideas?

Once the culltime is exceeded, I'd like to switch the group to PUBLIC rather than delete it entirely. So that there is the option for the old player to get back on and re-assign his group to PRIVATE.

ProgrammerDan commented 8 years ago

Every restart is fine.

The issue with public is while folks can get into chests and doors, they still can't break down walls. So it's equivalent to the open season on chests that deletion would cause, but doesn't address the ancient ruins that are impossible to remove problem.

Also I might be wrong, but I don't believe there is a command to change group from public to private available to players -- they would need to make a new group and merge the old one into it.

Also I stated this before, but I strongly disagree with actually decrementing reinforcements. I believe it should increment a scaling factor, so that hits cause more damage to existing reinforcement, but the underlying durability is unchanged. In this way, we aren't guaranteeing loss of reinforcement strength and forcing players to touch every block of their builds if they come back after a month's absence; but we also achieve our objective of allowing reinforcements to "effectively" degrade over time.

If you've already done the above, my apologies :D

Maxopoly commented 8 years ago

Yeah I dont think there is a command to change a group from public to private (though you can merge it into a private one).

I think the scaling break thing you suggested is a lot better gameplay wise and that would also massively decrease the resources this takes up, because we don't have to iterate over millions of reinforcements. We should probably do that instead of direct decay.

Scuwr commented 8 years ago

After thinking about it further, we already make a database check on every block break anyways and that doesn't seem to lag the server. Additionally, if we were to check after every server restart and set a value in the database, that still doesn't solve the problem of how we know whether the block break should be allowed.

With this in mind, I decided to directly implement an additional check in Citadel when a block break occurs, that invokes a NameLayer method to query the database for 'last_timestamp' which is now found in the 'faction' table. Then that timestamp is compared against a the cullTime variable stored within the Citadel.ReinforcementManager instance.

Due to the nature of coordinating between these two plugins, I also had to update the dependency version used to build Citadel from NameLayer1.5.4 to NameLayer1.5.43.

I am currently in the process of testing the changes made. After the cullTime has expired, any player can break reinforced blocks, but they also drop the reinforcement material. Do we want this as a feature? I see upsides of people "mining" old ruins for reinforcement materials, but at the same time, I can also see as being bad. Up to you really.

Scuwr commented 8 years ago

@ProgrammerDan Also, when a player logs back in, the last_timestamp is updated, so anything not destroyed while it was expired has its reinforcement renewed. I believe this would be in line with what you were talking about.

ProgrammerDan commented 8 years ago

@Scuwr It restores it back to the level it was before decay began? Or back to full strength regardless?

Also, thanks for diving in to this regardless -- it's great to see you wading in and kicking issue's asses.

Scuwr commented 8 years ago

@ProgrammerDan The change I made was very simple.

When the reinforcement has "expired", the ReinforcementDamageEvent is cancelled in the same way that /ctb handles block breaks. A reinforcement is determined as expired by comparing the last_timestamp with a configurable cullTime variable.

Once a player logs back in, the last_timestamp is updated, and the reinforcement is now back to the strength it had before.

In Citadel.BlockListener.BlockBreakEvent:

if (pr.isExpired()){
            is_cancelled = reinforcementBroken(player, rein);

In Citadel.PlayerReinforcement:

public boolean isExpired(){
    Timestamp ts = gm.getTimestamp(g.getName());
    if(ts.before(new Timestamp((new Date()).getTime() - Citadel.getReinforcementManager().getCulltime()))){
        return true;
    }

    return false;
}
ProgrammerDan commented 8 years ago

So the reinforcement is full strength up until it expires, at which point it is removed? I'm not sure I follow; full disclosure: I haven't reviewed your actual changes.

Perhaps I'll hold further comment until I have.

Scuwr commented 8 years ago

Yes, it's a bang-bang system. Reinforcements are on, and then they are off. But upon another login from a player with BLOCKS permission, the reinforcements are turned on again.

Maxopoly commented 8 years ago

It shouldn't completly turn them off suddenly, it should always be some sort of transistion. Also being able to ctb other reinforcements doesn't make much sense.

Instead of allowing to ctb the reinforcements, it should apply a multiplier on the amount of damage dealt to the reinforcement by a break, based on how long the group has been inactive.

Scuwr commented 8 years ago

Okay, how would you like it to scale? Preferably some exponential function based on the number of days past.

Maxopoly commented 8 years ago

We need a fancy function, that's a @ProgrammerDan task.

Scuwr commented 8 years ago

All I need is the ideal equation, then I can optimize it for the Java runtime environment (is this the fancy function you speak of?).

Scuwr commented 8 years ago

Here is something I came up with Damage = 2^(n/7) such that n/7 is truncated to an integer (ex: 9/7 = 1 or 20/21 = 2)

I figure after a month you should be able to destroy a Stone reinforced block with two hits. Around two months, you should be able to destroy an Iron reinforced block And after 3 months, you should be able to destroy a Diamond reinforced block.

Time Elapsed Damage per hit
0 Days 1 Damage
7 Days 2 Damage
14 Days 4 Damage
21 Days 8 Damage
28 Days 16 Damage

After 35 days, a Stone reinforced block will be destroyed with one hit. After 56 days, an Iron reinforced block will be destroyed with one hit. After 77 days, a Diamond reinforced block will be destroyed with one hit.

This function is very easy to implement since it uses powers of two. Each power can quickly be computed with a left-shift, making it very computational efficient.

Also for 2^(n/m), I can configure it to where m is a number that is retrieved from the config, so the exponential function could be modified easily for balancing purposes.

Maxopoly commented 8 years ago

Imo all of that should be increased by at least factor 5

Scuwr commented 8 years ago

Well that's why its configurable :)

And done! image

Scuwr commented 8 years ago

image

Scuwr commented 8 years ago

So does this look good to everyone?

ttk2 commented 8 years ago

I'd like to see this merged soon enough, reinforcement degradation is something we should at least play around with.

ProgrammerDan commented 8 years ago

Agreed, let me review the changes a bit more to doublecheck how the degredations work.

Maxopoly commented 8 years ago

https://github.com/Civcraft/NameLayer/pull/168