Code: Select all
Usage: /Su [ Player [ Rank [ Time ] ] ]
The command respects promote and demote limits, and can only be used by players with either of those. Players can su themselves, for which Permission.Demote is more loosely checked than when suing other players.
To reset a su, let the player log out, let the timer run out or use /Su PlayerName (without the rank). Your old rank info is preserved. Any rank changes during su are reset afterwards, the player who ranked gets informed about this.
Server crashes or incorrect shutdown may cause a players to lose their original rank. The original rank will, however, always be listed in their info as Previous Rank.
Command will still be fully compatible with 0.630 (and beyond).
Patch:
Show
Code: Select all
Index: fCraft/Player/Player.cs
===================================================================
--- fCraft/Player/Player.cs (revision 1639)
+++ fCraft/Player/Player.cs (working copy)
@@ -68,7 +68,31 @@
/// Deaf players can't hear anything. </summary>
public bool IsDeaf { get; set; }
-
+
+ #region [MOD] Su
+
+ /// <summary> Used by the custom Su command for permission checks and to restore player's rank
+ /// Do not change or even use unless you're purposely interacting with Su.
+ /// The player is by all other means ranked Info.Rank.</summary>
+ public Rank TrueRank { get; set; }
+ /// <summary> Used by the custom Su command to restore rankinfo.
+ /// Holds the Info.PreviousRank field. </summary>
+ public Rank TruePreviousRank { get; set; }
+ /// <summary> Used by the custom Su command to restore rankinfo.
+ /// Holds the Info.PromotedBy field. </summary>
+ public string TrueRanker { get; set; }
+ /// <summary> Used by the custom Su command to restore rankinfo.
+ /// Holds the Info.RankChangeReason field. </summary>
+ public string TrueRankReason { get; set; }
+ /// <summary> Used by the custom Su command to restore rankinfo.
+ /// Holds the Info.RankChangeDate field. </summary>
+ public DateTime TrueRankDate { get; set; }
+ /// <summary> Used by the custom Su command to restore rankinfo.
+ /// Holds the Info.RankChangeType field. </summary>
+ public RankChangeType TrueRankType { get; set; }
+
+ #endregion
+
/// <summary> The world that the player is currently on. May be null.
/// Use .JoinWorld() to make players teleport to another world. </summary>
[CanBeNull]
Index: fCraft/Commands/MaintenanceCommands.cs
===================================================================
--- fCraft/Commands/MaintenanceCommands.cs (revision 1639)
+++ fCraft/Commands/MaintenanceCommands.cs (working copy)
@@ -1,4 +1,4 @@
-// Copyright 2009-2012 Matvei Stefarov <[email protected]>
+// Copyright 2009-2012 Matvei Stefarov <[email protected]>
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -18,7 +18,10 @@
CommandManager.RegisterCommand( CdMassRank );
CommandManager.RegisterCommand( CdAutoRankAll );
CommandManager.RegisterCommand( CdSetInfo );
-
+
+ CommandManager.RegisterCustomCommand( CdSu );
+ Server.Started += SuInit;
+
CommandManager.RegisterCommand( CdReload );
CommandManager.RegisterCommand( CdShutdown );
@@ -70,7 +73,279 @@
#endif
}
+
+ #region [MOD] Su
+
+ /// <summary> Initializes the Su command by subscribing to events and getting the console pseudoplayer ready.
+ /// To be called after Server.Started </summary>
+ static void SuInit( object sender, System.EventArgs e ) {
+ // These needn't be after Server.Started, but it's best to have everything in the same place.
+ PlayerInfo.RankChanging += UpdateTrueRankHandler;
+ Player.Ready += UpdateTrueRankHandler;
+ Player.Disconnected += UnSuHandler; // Do not comment, TrueRank info is lost at logout.
+
+ // This needs be after Server.Started, when Player.Console is ready.
+ UpdateTrueRankHandler( null, new Events.PlayerEventArgs( Player.Console ) );
+ }
+
+ /// <summary> Used to register the custom Su command with. </summary>
+ static readonly CommandDescriptor CdSu = new CommandDescriptor {
+ Name = "Su",
+ Aliases = new[] {"temprank", "sudo"},
+ Category = CommandCategory.Moderation,
+ IsConsoleSafe = true,
+ NotRepeatable = true,
+ Help = "Allows a player to tempomarily become a different rank.",
+ Usage = "/Su [ Player [ Rank [ Time ] ] ]", // TODO: time | ; command
+ Handler = SuHandler
+ };
+
+ /// <summary> Code to be executed when <code>Player player</code> issues <code>/Su <params></code> </summary>
+ static void SuHandler( Player player, CommandReader cmd )
+ {
+ Player target = cmd.HasNext ? Server.FindPlayerOrPrintMatches( player, cmd.Next(), true, true ) : player;
+ if( target == null || target == Player.Console ) {
+ return;
+ }
+
+ string rank;
+ Rank reqrank = cmd.HasNext ? RankManager.FindRank( rank = cmd.Next() ) : target.TrueRank;
+ if( reqrank == null ) {
+ player.MessageNoRank( rank );
+ return;
+ }
+ rank = null; // GC ?
+
+ bool suself = player == target;
+ bool unsu = reqrank == target.TrueRank;
+
+ if( target.Info.Rank == reqrank ) {
+ player.Message( String.Format( "&SPlayer {0}&S is already ranked {1}",
+ target.Info.DisplayedName ?? target.ClassyName,
+ reqrank.ClassyName ) );
+ return;
+ }
+
+ if( unsu && cmd.HasNext )
+ {
+ player.Message( "&SCannot specify time when un&HSu&Sing." );
+ return;
+ }
+ // TODO: add nested timers.
+ if( !cmd.IsConfirmed && target.Info.Rank != target.TrueRank && cmd.HasNext ) {
+ player.Confirm( cmd, "&SPlayer {0}&S will be un&HSu&Sed to {1}&S after the specified time.",
+ target.Info.DisplayedName ?? target.ClassyName,
+ target.TrueRank.ClassyName );
+ return;
+ }
+
+ TimeSpan time = cmd.HasNext ? cmd.Next().TryParseMiniTimespan(out time) ? time : TimeSpan.Zero : TimeSpan.Zero;
+ if( time != TimeSpan.Zero ) {
+ Scheduler.NewTask( UnSuHandler, (object)target ).RunOnce( time );
+ } else if( !unsu ) {
+ // Uncomment the following for a mandatory timer
+// CdSu.PrintUsage( player );
+// return;
+ }
+
+ if( !player.IsSuper ) // don't skip permission checks unless allmighty.
+ {
+ // PERMISSION CHECKS
+ if( suself ) {
+ // Promoting yourself if permission.promote allows it.
+ if( reqrank > player.TrueRank &&
+ !player.TrueRank.Can( Permission.Promote, reqrank ) ) {
+ if( player.TrueRank.Can(Permission.Promote ) ) {
+ player.Message( "Cannot &H/Su&S yourself to {0}&s: you may only promote up to rank {1}&s.",
+ reqrank.ClassyName,
+ player.TrueRank.GetLimit( Permission.Promote ).ClassyName );
+ } else {
+ player.Message( "&SYou need to be ranked {0}&S+ to demote.",
+ RankManager.GetMinRankWithAllPermissions( Permission.Promote ).ClassyName );
+ }
+ return;
+ }
+
+ // Demoting yourself to any rank with any permission.demote
+ if( reqrank < player.TrueRank && !player.TrueRank.Can( Permission.Demote ) ) {
+ player.Message( "&SYou need to be ranked {0}&S+ to demote.",
+ RankManager.GetMinRankWithAllPermissions( Permission.Demote ).ClassyName );
+ return;
+ }
+
+ // Unsueing yourself, only if you were able to Su yourself there.
+ if( unsu &&
+ ( target.Info.Rank > target.TrueRank ? !player.TrueRank.Can( Permission.Promote, player.Info.Rank ) : !player.TrueRank.Can( Permission.Demote ) ) ) {
+ if( target.Info.Rank < target.TrueRank ) {
+ player.Message( "&SYou need to be ranked {0}&S+ to &H/Su&s.",
+ RankManager.GetMinRankWithAnyPermission( Permission.Promote, Permission.Demote ).ClassyName );
+ } else {
+ player.Message( "&SCannot un&HSu&S yourself as {0}&S, you may only un&HSu&S if you could have &HSu&Sed yourself in the first place.",
+ target.Info.Rank.ClassyName );
+ }
+ return;
+ }
+ } else {
+ // Promoting someone else, if permission.promote allows it.
+ if( reqrank > target.TrueRank &&
+ !player.TrueRank.Can( Permission.Promote, reqrank ) ) {
+ if( player.TrueRank.Can( Permission.Promote ) ) {
+ player.Message( "Cannot &H/Su&S {0}&S to {1}&s: you may only promote up to rank {2}&s.",
+ target.Info.DisplayedName ?? target.ClassyName,
+ reqrank.ClassyName, player.TrueRank.GetLimit( Permission.Promote ).ClassyName );
+ } else {
+ player.Message( "&SYou need to be ranked {0}&S+ to promote.",
+ RankManager.GetMinRankWithAllPermissions( Permission.Promote ).ClassyName );
+ }
+ return;
+ }
+
+ // Demoting someone else, if permission.demote allows it.
+ if( reqrank < target.TrueRank &&
+ ! player.TrueRank.Can( Permission.Demote, target.TrueRank ) ) {
+ if( player.TrueRank.Can( Permission.Demote ) ) {
+ player.Message( "&SCannot demote {0}&S (ranked {1}&S): you may only demote players ranked {2}&S or below.",
+ target.Info.DisplayedName ?? target.ClassyName,
+ target.TrueRank.ClassyName,
+ player.TrueRank.GetLimit( Permission.Demote ).ClassyName );
+ } else {
+ player.Message( "&SYou need to be ranked {0}&S+ to demote.",
+ RankManager.GetMinRankWithAllPermissions( Permission.Demote ).ClassyName );
+ }
+ return;
+ }
+
+ //Unsueing someone else, if you were able to set the player's rank to the current.
+ if( unsu &&
+ ( target.Info.Rank < target.TrueRank ? !player.TrueRank.Can( Permission.Demote, target.TrueRank ) : !player.TrueRank.Can( Permission.Promote, target.Info.Rank ) ) )
+ {
+ player.Message( "&SCannot un&HSu&S player {0}&S (ranked {1}&S), you may only un&HSu&S players that you could have &HSu&Sed to {2}&S in the first place.",
+ target.Info.DisplayedName ?? target.ClassyName,
+ target.TrueRank.ClassyName,
+ target.Info.Rank.ClassyName );
+ return;
+ }
+ }
+ }
+
+ // whether or not the change is a promotion from the server's point of view.
+ // saved here because info is lost after processing the change
+ bool effectivepromote = reqrank > target.Info.Rank;
+
+ // Ensure correct name shows up
+ Player.AutoRank.Info.DisplayedName = suself ? player.Info.DisplayedName ?? player.ClassyName : target.TrueRanker;
+ // Process Rank
+ target.Info.ChangeRank( suself || (reqrank > target.TrueRank ? !player.TrueRank.Can( Permission.Promote, target.Info.Rank ) : !player.TrueRank.Can( Permission.Demote, target.Info.Rank ) ) ? Player.AutoRank : player,
+ reqrank,
+ unsu ? target.TrueRankReason : String.Format( "{0}&s: &H/Su &s{1} &s{2}",
+ player.Info.DisplayedName ?? player.ClassyName,
+ target.Info.DisplayedName ?? target.ClassyName,
+ reqrank.ClassyName ),
+ false,
+ false,
+ unsu );
+ Player.AutoRank.Info.DisplayedName = "";
+
+ // Restore Info
+ if( unsu ) {
+ target.Info.RankChangeDate = target.TrueRankDate;
+ target.Info.RankChangeType = target.TrueRankType;
+ target.Info.PreviousRank = target.TruePreviousRank;
+ }
+ else
+ {
+ target.Info.PreviousRank = target.TrueRank; // always list true rank in info
+ }
+
+ string message = String.Format( "{0}&S was {1}&S {2}&Sto {3}&S{4}&S by {5}&S{6}&S.",
+ target.Info.DisplayedName ?? target.ClassyName,
+ effectivepromote ? "promoted" : "demoted",
+ unsu ? "back " : "",
+ reqrank.ClassyName,
+ time != TimeSpan.Zero ? String.Format(" for {0}", time.ToMiniString() ) : unsu ? "" : " until logout",
+ player.Info.DisplayedName ?? player.ClassyName,
+ suself ? " self" : "" );
+
+ // Broadcast rank change.
+ if( ConfigKey.AnnounceRankChanges.Enabled() ) {
+ Server.Message( message );
+ } else {
+ player.Message( message );
+ if( !suself ) target.Message( message );
+ }
+ }
+
+ /// <summary> Updates the information Su uses. </summary>
+ // Called by player login
+ static void UpdateTrueRankHandler( object sender, Events.PlayerEventArgs e )
+ {
+ e.Player.TrueRank = e.Player.Info.Rank ?? RankManager.DefaultRank;
+ e.Player.TrueRanker = e.Player.Info.RankChangedBy ?? "";
+ e.Player.TrueRankReason = e.Player.Info.RankChangeReason ?? (ConfigKey.RequireRankChangeReason.Enabled() ? "UnSued." : null );
+ e.Player.TruePreviousRank = e.Player.Info.PreviousRank ?? RankManager.DefaultRank;
+ e.Player.TrueRankType = e.Player.Info.RankChangeType;
+ e.Player.TrueRankDate = e.Player.Info.RankChangeDate;
+ }
+
+ // Called by rank change
+ static void UpdateTrueRankHandler( object sender, Events.PlayerInfoRankChangingEventArgs e )
+ {
+ if( e.PlayerInfo.PlayerObject == null ) return; // Not online
+ if( e.PlayerInfo.Rank != e.PlayerInfo.PlayerObject.TrueRank ) // sued?
+ {
+ e.RankChanger.Message( String.Format("{0} is &H/Su&Sed, this promotion will be reverted on logout or &H/Su {1}",
+ e.PlayerInfo.DisplayedName ?? e.PlayerInfo.ClassyName,
+ e.PlayerInfo.Name ) );
+ }
+ else
+ {
+ e.PlayerInfo.PlayerObject.TrueRank = e.NewRank;
+ e.PlayerInfo.PlayerObject.TrueRanker = e.RankChanger.Info.DisplayedName ?? e.RankChanger.Info.ClassyName;
+ e.PlayerInfo.PlayerObject.TrueRankReason = e.Reason;
+ e.PlayerInfo.PlayerObject.TruePreviousRank = e.OldRank;
+ e.PlayerInfo.PlayerObject.TrueRankType = e.RankChangeType;
+ e.PlayerInfo.PlayerObject.TrueRankDate = DateTime.UtcNow;
+ }
+ }
+
+ /// <summary> Gives a player their original rank back.
+ /// Part of the custom Su command. </summary>
+ // Called by Timer
+ static void UnSuHandler( SchedulerTask task )
+ {
+ Player player = (Player)task.UserState;
+ if( player.TrueRank == player.Info.Rank ) return; // already unsued
+
+ // Process change
+ Player.AutoRank.Info.DisplayedName = player.TrueRanker;
+ player.Info.ChangeRank( Player.AutoRank, player.TrueRank , player.TrueRankReason, false, false, true );
+ Player.AutoRank.Info.DisplayedName = "";
+
+ // Update info
+ player.Info.RankChangeDate = player.TrueRankDate;
+ player.Info.PreviousRank = player.TruePreviousRank;
+ player.Info.RankChangeType = player.TrueRankType;
+ }
+
+ // Called by Logout
+ static void UnSuHandler( object sender, Events.PlayerDisconnectedEventArgs e )
+ {
+ if( !e.IsFake && ( e.Player.TrueRank != e.Player.Info.Rank ) ) {
+ // Process change
+ Player.AutoRank.Info.DisplayedName = e.Player.TrueRanker;
+ e.Player.Info.ChangeRank( Player.AutoRank, e.Player.TrueRank , e.Player.TrueRankReason, false, false, true );
+ Player.AutoRank.Info.DisplayedName = "";
+
+ //update info
+ e.Player.Info.RankChangeDate = e.Player.TrueRankDate;
+ e.Player.Info.PreviousRank = e.Player.TruePreviousRank;
+ e.Player.Info.RankChangeType = e.Player.TrueRankType;
+ }
+ }
+
+ #endregion
+
#region DumpStats
static readonly CommandDescriptor CdDumpStats = new CommandDescriptor {
Show
Add In Player.cs, line 68:
Add in MaintenanceCommands.cs, line 18:
Add in MaintenanceCommands.cs, line 73:
Code: Select all
#region [MOD] Su
/// <summary> Used by the custom Su command for permission checks and to restore player's rank
/// Do not change or even use unless you're purposely interacting with Su.
/// The player is by all other means ranked Info.Rank.</summary>
public Rank TrueRank { get; set; }
/// <summary> Used by the custom Su command to restore rankinfo.
/// Holds the Info.PreviousRank field. </summary>
public Rank TruePreviousRank { get; set; }
/// <summary> Used by the custom Su command to restore rankinfo.
/// Holds the Info.PromotedBy field. </summary>
public string TrueRanker { get; set; }
/// <summary> Used by the custom Su command to restore rankinfo.
/// Holds the Info.RankChangeReason field. </summary>
public string TrueRankReason { get; set; }
/// <summary> Used by the custom Su command to restore rankinfo.
/// Holds the Info.RankChangeDate field. </summary>
public DateTime TrueRankDate { get; set; }
/// <summary> Used by the custom Su command to restore rankinfo.
/// Holds the Info.RankChangeType field. </summary>
public RankChangeType TrueRankType { get; set; }
#endregion
Code: Select all
CommandManager.RegisterCustomCommand( CdSu );
Server.Started += SuInit;
Code: Select all
#region [MOD] Su
/// <summary> Initializes the Su command by subscribing to events and getting the console pseudoplayer ready.
/// To be called after Server.Started </summary>
static void SuInit( object sender, System.EventArgs e ) {
// These needn't be after Server.Started, but it's best to have everything in the same place.
PlayerInfo.RankChanging += UpdateTrueRankHandler;
Player.Ready += UpdateTrueRankHandler;
Player.Disconnected += UnSuHandler; // Do not comment, TrueRank info is lost at logout.
// This needs be after Server.Started, when Player.Console is ready.
UpdateTrueRankHandler( null, new Events.PlayerEventArgs( Player.Console ) );
}
/// <summary> Used to register the custom Su command with. </summary>
static readonly CommandDescriptor CdSu = new CommandDescriptor {
Name = "Su",
Aliases = new[] {"temprank", "sudo"},
Category = CommandCategory.Moderation,
IsConsoleSafe = true,
NotRepeatable = true,
Help = "Allows a player to tempomarily become a different rank.",
Usage = "/Su [ Player [ Rank [ Time ] ] ]", // TODO: time | ; command
Handler = SuHandler
};
/// <summary> Code to be executed when <code>Player player</code> issues <code>/Su <params></code> </summary>
static void SuHandler( Player player, CommandReader cmd )
{
Player target = cmd.HasNext ? Server.FindPlayerOrPrintMatches( player, cmd.Next(), true, true ) : player;
if( target == null || target == Player.Console ) {
return;
}
string rank;
Rank reqrank = cmd.HasNext ? RankManager.FindRank( rank = cmd.Next() ) : target.TrueRank;
if( reqrank == null ) {
player.MessageNoRank( rank );
return;
}
rank = null; // GC ?
bool suself = player == target;
bool unsu = reqrank == target.TrueRank;
if( target.Info.Rank == reqrank ) {
player.Message( String.Format( "&SPlayer {0}&S is already ranked {1}",
target.Info.DisplayedName ?? target.ClassyName,
reqrank.ClassyName ) );
return;
}
if( unsu && cmd.HasNext )
{
player.Message( "&SCannot specify time when un&HSu&Sing." );
return;
}
// TODO: add nested timers.
if( !cmd.IsConfirmed && target.Info.Rank != target.TrueRank && cmd.HasNext ) {
player.Confirm( cmd, "&SPlayer {0}&S will be un&HSu&Sed to {1}&S after the specified time.",
target.Info.DisplayedName ?? target.ClassyName,
target.TrueRank.ClassyName );
return;
}
TimeSpan time = cmd.HasNext ? cmd.Next().TryParseMiniTimespan(out time) ? time : TimeSpan.Zero : TimeSpan.Zero;
if( time != TimeSpan.Zero ) {
Scheduler.NewTask( UnSuHandler, (object)target ).RunOnce( time );
} else if( !unsu ) {
// Uncomment the following for a mandatory timer
// CdSu.PrintUsage( player );
// return;
}
if( !player.IsSuper ) // don't skip permission checks unless allmighty.
{
// PERMISSION CHECKS
if( suself ) {
// Promoting yourself if permission.promote allows it.
if( reqrank > player.TrueRank &&
! player.TrueRank.Can( Permission.Promote, reqrank ) ) {
if( player.TrueRank.Can(Permission.Promote ) ) {
player.Message( "Cannot &H/Su&S yourself to {0}&s: you may only promote up to rank {1}&s.",
reqrank.ClassyName,
player.TrueRank.GetLimit( Permission.Promote ).ClassyName );
} else {
player.Message( "&SYou need to be ranked {0}&S+ to demote.",
RankManager.GetMinRankWithAllPermissions( Permission.Promote ).ClassyName );
}
return;
}
// Demoting yourself to any rank with any permission.demote
if( reqrank < player.TrueRank && !player.TrueRank.Can( Permission.Demote ) ) {
player.Message( "&SYou need to be ranked {0}&S+ to demote.",
RankManager.GetMinRankWithAllPermissions( Permission.Demote ).ClassyName );
return;
}
// Unsueing yourself, only if you were able to Su yourself there.
if( unsu &&
( target.Info.Rank > target.TrueRank ? !player.TrueRank.Can( Permission.Promote, player.Info.Rank ) : !player.TrueRank.Can( Permission.Demote ) ) ) {
if( target.Info.Rank < target.TrueRank ) {
player.Message( "&SYou need to be ranked {0}&S+ to &H/Su&s.",
RankManager.GetMinRankWithAnyPermission( Permission.Promote, Permission.Demote ).ClassyName );
} else {
player.Message( "&SCannot un&HSu&S yourself as {0}&S, you may only un&HSu&S if you could have &HSu&Sed yourself in the first place.",
target.Info.Rank.ClassyName );
}
return;
}
} else {
// Promoting someone else, if permission.promote allows it.
if( reqrank > target.TrueRank &&
! player.TrueRank.Can( Permission.Promote, reqrank ) ) {
if( player.TrueRank.Can( Permission.Promote ) ) {
player.Message( "Cannot &H/Su&S {0}&S to {1}&s: you may only promote up to rank {2}&s.",
target.Info.DisplayedName ?? target.ClassyName,
reqrank.ClassyName, player.TrueRank.GetLimit( Permission.Promote ).ClassyName );
} else {
player.Message( "&SYou need to be ranked {0}&S+ to promote.",
RankManager.GetMinRankWithAllPermissions( Permission.Promote ).ClassyName );
}
return;
}
// Demoting someone else, if permission.demote allows it.
if( reqrank < target.TrueRank &&
! player.TrueRank.Can( Permission.Demote, target.TrueRank ) ) {
if( player.TrueRank.Can( Permission.Demote ) ) {
player.Message( "&SCannot demote {0}&S (ranked {1}&S): you may only demote players ranked {2}&S or below.",
target.Info.DisplayedName ?? target.ClassyName,
target.TrueRank.ClassyName,
player.TrueRank.GetLimit( Permission.Demote ).ClassyName );
} else {
player.Message( "&SYou need to be ranked {0}&S+ to demote.",
RankManager.GetMinRankWithAllPermissions( Permission.Demote ).ClassyName );
}
return;
}
//Unsueing someone else, if you were able to set the player's rank to the current.
if( unsu &&
( target.Info.Rank < target.TrueRank ? !player.TrueRank.Can( Permission.Demote, target.TrueRank ) : !player.TrueRank.Can( Permission.Promote, target.Info.Rank ) ) )
{
player.Message( "&SCannot un&HSu&S player {0}&S (ranked {1}&S), you may only un&HSu&S players that you could have &HSu&Sed to {2}&S in the first place.",
target.Info.DisplayedName ?? target.ClassyName,
target.TrueRank.ClassyName,
target.Info.Rank.ClassyName );
return;
}
}
}
// whether or not the change is a promotion from the server's point of view.
// saved here because info is lost after processing the change
bool effectivepromote = reqrank > target.Info.Rank;
// Ensure correct name shows up
Player.AutoRank.Info.DisplayedName = suself ? player.Info.DisplayedName ?? player.ClassyName : target.TrueRanker;
// Process Rank
target.Info.ChangeRank( suself || (reqrank > target.TrueRank ? !player.TrueRank.Can( Permission.Promote, target.Info.Rank ) : !player.TrueRank.Can( Permission.Demote, target.Info.Rank ) ) ? Player.AutoRank : player,
reqrank,
unsu ? target.TrueRankReason : String.Format( "{0}&s: &H/Su &s{1} &s{2}",
player.Info.DisplayedName ?? player.ClassyName,
target.Info.DisplayedName ?? target.ClassyName,
reqrank.ClassyName ),
false,
false,
unsu );
Player.AutoRank.Info.DisplayedName = "";
// Restore Info
if( unsu ) {
target.Info.RankChangeDate = target.TrueRankDate;
target.Info.RankChangeType = target.TrueRankType;
target.Info.PreviousRank = target.TruePreviousRank;
}
else
{
target.Info.PreviousRank = target.TrueRank; // always list true rank in info
}
string message = String.Format( "{0}&S was {1}&S {2}&Sto {3}&S{4}&S by {5}&S{6}&S.",
target.Info.DisplayedName ?? target.ClassyName,
effectivepromote ? "Promoted" : "Demoted",
unsu ? "Back " : "",
reqrank.ClassyName,
time != TimeSpan.Zero ? String.Format(" for {0}", time.ToMiniString() ) : unsu ? "" : " until logout",
player.Info.DisplayedName ?? player.ClassyName,
suself ? " self" : "" );
// Broadcast rank change.
if( ConfigKey.AnnounceRankChanges.Enabled() ) {
Server.Message( message );
} else {
player.Message( message );
if( !suself ) target.Message( message );
}
}
/// <summary> Updates the information Su uses. </summary>
// Called by player login
static void UpdateTrueRankHandler( object sender, Events.PlayerEventArgs e )
{
e.Player.TrueRank = e.Player.Info.Rank ?? RankManager.DefaultRank;
e.Player.TrueRanker = e.Player.Info.RankChangedBy ?? "";
e.Player.TrueRankReason = e.Player.Info.RankChangeReason ?? (ConfigKey.RequireRankChangeReason.Enabled() ? "UnSued." : null );
e.Player.TruePreviousRank = e.Player.Info.PreviousRank ?? RankManager.DefaultRank;
e.Player.TrueRankType = e.Player.Info.RankChangeType;
e.Player.TrueRankDate = e.Player.Info.RankChangeDate;
}
// Called by rank change
static void UpdateTrueRankHandler( object sender, Events.PlayerInfoRankChangingEventArgs e )
{
if( e.PlayerInfo.PlayerObject == null ) return; // Not online
if( e.PlayerInfo.Rank != e.PlayerInfo.PlayerObject.TrueRank ) // sued?
{
e.RankChanger.Message( String.Format("{0} is &H/Su&Sed, this promotion will be reverted on logout or &H/Su {1}",
e.PlayerInfo.DisplayedName ?? e.PlayerInfo.ClassyName,
e.PlayerInfo.Name ) );
}
else
{
e.PlayerInfo.PlayerObject.TrueRank = e.NewRank;
e.PlayerInfo.PlayerObject.TrueRanker = e.RankChanger.Info.DisplayedName ?? e.RankChanger.Info.ClassyName;
e.PlayerInfo.PlayerObject.TrueRankReason = e.Reason;
e.PlayerInfo.PlayerObject.TruePreviousRank = e.OldRank;
e.PlayerInfo.PlayerObject.TrueRankType = e.RankChangeType;
e.PlayerInfo.PlayerObject.TrueRankDate = DateTime.UtcNow;
}
}
/// <summary> Gives a player their original rank back.
/// Part of the custom Su command. </summary>
// Called by Timer
static void UnSuHandler( SchedulerTask task )
{
Player player = (Player)task.UserState;
if( player.TrueRank == player.Info.Rank ) return; // already unsued
// Process change
Player.AutoRank.Info.DisplayedName = player.TrueRanker;
player.Info.ChangeRank( Player.AutoRank, player.TrueRank , player.TrueRankReason, false, false, true );
Player.AutoRank.Info.DisplayedName = "";
// Update info
player.Info.RankChangeDate = player.TrueRankDate;
player.Info.PreviousRank = player.TruePreviousRank;
player.Info.RankChangeType = player.TrueRankType;
}
// Called by Logout
static void UnSuHandler( object sender, Events.PlayerDisconnectedEventArgs e )
{
if( e.Player.TrueRank != e.Player.Info.Rank ) {
// Process change
Player.AutoRank.Info.DisplayedName = e.Player.TrueRanker;
e.Player.Info.ChangeRank( Player.AutoRank, e.Player.TrueRank , e.Player.TrueRankReason, false, false, true );
Player.AutoRank.Info.DisplayedName = "";
//update info
e.Player.Info.RankChangeDate = e.Player.TrueRankDate;
e.Player.Info.PreviousRank = e.Player.TruePreviousRank;
e.Player.Info.RankChangeType = e.Player.TrueRankType;
}
}
#endregion
- Fixed a crash where a player with no existing RankChangeReason was sued on a server where those are required.