-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simplify and improve Rude. #1
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Fancyauth.APIUtil; | ||
using Fancyauth.API; | ||
using System.Collections.Concurrent; | ||
|
||
namespace Rude | ||
{ | ||
public class RudePlugin : PluginBase | ||
{ | ||
private readonly string[] Prefixes = { "[Rude] ", "[Ruderer] ", "[Rüdiger] " }; | ||
public const double MinRudeTime = 45; | ||
public const double MaxRudeTime = 120; | ||
public const double MinRudeActionTimeout = 3; | ||
public const double MaxRudeActionTimeout = 18; | ||
|
||
private ConcurrentDictionary<int, RudeStatus> RudeStatus = new ConcurrentDictionary<int, RudeStatus>(); | ||
private ConcurrentDictionary<int, RudeStatus> RudeActorStatus = new ConcurrentDictionary<int, RudeStatus>(); | ||
|
||
private Random rand = new Random(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The primary reason why all code in fancyauth everywhere either instantiates a new (Also, is this really called |
||
|
||
[ContextCallback("Rude")] | ||
public async Task RudeUser(IUser actor, IUserShim targetShim) | ||
{ | ||
var target = await targetShim.Load(); | ||
EnsureRudeStatus(target.UserId); | ||
EnsureRudeStatus(actor.UserId); | ||
|
||
var rude = RudeStatus[target.UserId]; | ||
var rudeActor = RudeActorStatus[actor.UserId]; | ||
|
||
if (rudeActor.RudeLevel > 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you even stop and think about this for a second? Because unless I'm dumb this is utterly broken: The first level here means that your rude is on cooldown and lasts for the cooldown period. But as implemented, attempting to rude with your rude on cooldown adds additional levels for the full rude duration as cooldown. Also this is completely independent and never actually shows up anywhere, wtf. |
||
{ | ||
var actorRude = RudeStatus[actor.UserId]; | ||
|
||
// You shouldn't be able to use this to reset yourself | ||
if (rudeActor.RudeLevel < Prefixes.Length) | ||
actorRude.RaiseRudeLevel(RandomTimespan(MinRudeTime, MaxRudeTime)); | ||
|
||
return; | ||
} | ||
|
||
rudeActor.RaiseRudeLevel(RandomTimespan(MinRudeActionTimeout, MaxRudeActionTimeout)); | ||
rude.RaiseRudeLevel(RandomTimespan(MinRudeTime, MaxRudeTime)); | ||
|
||
if (rude.RudeLevel > Prefixes.Length) | ||
{ | ||
rude.Reset(); | ||
await target.Kick("You have been too rude."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI in the context of guests this leaks memory as every new guest has a new userid. The best solution would be to write this to the database but in any case, guests' data has to be freed (for members keeping it is acceptable but still not pretty). |
||
} | ||
else | ||
{ | ||
target.Name = GetUserName(target); | ||
await target.SaveChanges(); | ||
} | ||
} | ||
|
||
public override async Task OnUserConnected(IUser user) | ||
{ | ||
user.Name = GetUserName(user); | ||
await user.SaveChanges(); | ||
} | ||
|
||
private void EnsureRudeStatus(int userId) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tbh this whole function is ill-designed because let's face it are you ever going to call this and then not use a Just call |
||
{ | ||
if (!RudeStatus.ContainsKey(userId)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This check is pointless. Not only is it TOCTTOU-racy but it also accomplishes nothing at all - |
||
{ | ||
RudeStatus.TryAdd(userId, new RudeStatus | ||
{ | ||
OnRudeLevelDecrease = async () => | ||
{ | ||
var users = await Server.GetOnlineUsers(); | ||
var user = users.SingleOrDefault(a => a.UserId == userId); | ||
if (user == null) | ||
return; | ||
|
||
user.Name = GetUserName(user); | ||
await user.SaveChanges(); | ||
} | ||
}); | ||
} | ||
|
||
if (!RudeActorStatus.ContainsKey(userId)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same |
||
{ | ||
RudeActorStatus.TryAdd(userId, new RudeStatus | ||
{ | ||
OnRudeLevelDecrease = () => { } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My opinion stands. Reusing this data structure for cooldowns feels very bolted-on so this callback still carries no value IMO. The class's one big feature is the delay mechanism where you can run code after a timeout and abstracting that might even make sense if you do it on a general layer (where it's no longer specific to rude) but that still doesn't give you a valid reason to use it here and without that, it's kinda pointless. |
||
}); | ||
} | ||
} | ||
|
||
private string GetUserName(IUser user) | ||
{ | ||
EnsureRudeStatus(user.UserId); | ||
|
||
string name = user.Name; | ||
|
||
// Remove potential old prefix | ||
var startsWith = Prefixes.SingleOrDefault(a => name.StartsWith(a)); | ||
if (startsWith != null) | ||
name = name.Substring(startsWith.Length); | ||
|
||
var level = RudeStatus[user.UserId].RudeLevel; | ||
|
||
if (level > 0) | ||
return Prefixes[level - 1] + name; | ||
|
||
|
||
return name; | ||
} | ||
|
||
private TimeSpan RandomTimespan(double minMinues, double maxMinutes) | ||
{ | ||
return TimeSpan.FromMinutes(minMinues + rand.NextDouble() * (maxMinutes - minMinues)); | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
TimeSpan
here might make this a little more obvious.