Administrator's Toolkit VS plugin
修订版 | ca0f0d1c56f86ab1dddfe64e3900f6743b50b603 (tree) |
---|---|
时间 | 2022-06-22 10:18:57 |
作者 | ![]() |
Commiter | melchior |
Idler W.I.P. ,
@@ -54,6 +54,12 @@ namespace AdminToolkit | ||
54 | 54 | /// </summary> |
55 | 55 | public bool VariableSpawnpoints { get; set; } |
56 | 56 | |
57 | + /// <summary> | |
58 | + /// Time to permit players to Idle (no movement), before Auto-kick | |
59 | + /// </summary> | |
60 | + /// <value>Time span</value> | |
61 | + public TimeSpan IdleTimeout { get; set; } | |
62 | + | |
57 | 63 | public AdminModConfig( ) |
58 | 64 | { |
59 | 65 | this.PlayerRoleRestrain = "suvisitor"; |
@@ -64,6 +70,7 @@ namespace AdminToolkit | ||
64 | 70 | this.BackupDelay = new TimeSpan(4, 0, 0); |
65 | 71 | this.BackupCount = 6; |
66 | 72 | this.Autobackup = false; |
73 | + this.IdleTimeout = new TimeSpan(0, 0, 0);// 0 = OFF | |
67 | 74 | } |
68 | 75 | |
69 | 76 |
@@ -83,6 +83,7 @@ | ||
83 | 83 | <Compile Include="Commands\VariableSpawnpoints.cs" /> |
84 | 84 | <Compile Include="ATK_BasicFeatures.cs" /> |
85 | 85 | <Compile Include="Helpers\MultiLang.cs" /> |
86 | + <Compile Include="Commands\IdlePlayerControl.cs" /> | |
86 | 87 | </ItemGroup> |
87 | 88 | <ItemGroup> |
88 | 89 | <Folder Include="VS_libs\" /> |
@@ -28,10 +28,10 @@ namespace AdminToolkit | ||
28 | 28 | * [DONE] Keeps track of Elapsed game calendar days for players |
29 | 29 | * [DONE] Alter ADMIN text to look 'different' (optional) |
30 | 30 | * [DONE] Cyclic automatic Backups |
31 | - * [WIP} Broadcast messages, on a schedule ?????? | |
32 | - * [???] Custom Server name : custom formats to indicate server state/time/things | |
33 | - * [IDEA]: Player VIRTUAL (temp) Inventory; Move Player inventory to alternate [world] D.B. as Inventory backup | |
34 | - * [WIP]: Variable Player (re)Spawn points from a list ~ at random | |
31 | + * [WIP] Broadcast messages, on a schedule ?????? | |
32 | + * [RSCH] Custom Server name : custom formats to indicate server state/time/season/things | |
33 | + * [DONE?]: Variable Player (re)Spawn points from a list ~ at random | |
34 | + * [RSCH] Idle player eject on time(out) | |
35 | 35 | * |
36 | 36 | * WORKAROUND -- Localized messages are by SERVERS Native Language code...Not the players... |
37 | 37 | */ |
@@ -109,6 +109,8 @@ namespace AdminToolkit | ||
109 | 109 | this.ServerAPI.RegisterCommand(new BackupCycleCommand(this.ServerAPI) ); |
110 | 110 | //this.ServerAPI.RegisterCommand(new BannerControl(this.ServerAPI)); |
111 | 111 | this.ServerAPI.RegisterCommand(new PingerCommand(this.ServerAPI)); |
112 | + this.ServerAPI.RegisterCommand(new IdlePlayerControl(this.ServerAPI)); | |
113 | + | |
112 | 114 | if (this.CachedConfiguration.VariableSpawnpoints) { this.ServerAPI.RegisterCommand(new VariableSpawnpoints(this.ServerAPI)); } |
113 | 115 | |
114 | 116 | if (CachedConfiguration.BoomingVoice) { |
@@ -0,0 +1,124 @@ | ||
1 | +using System; | |
2 | + | |
3 | +using System.Linq; | |
4 | +using System.Text; | |
5 | +using System.Collections.Generic; | |
6 | + | |
7 | +using Vintagestory.API.Common; | |
8 | +using Vintagestory.API.Server; | |
9 | +using Vintagestory.Server; | |
10 | +using Vintagestory.API.Common.Entities; | |
11 | + | |
12 | +namespace AdminToolkit | |
13 | +{ | |
14 | + public class IdlePlayerControl : AdminModCommand | |
15 | + { | |
16 | + const string idleKick_Key = @"atk:idle-kick"; | |
17 | + | |
18 | + uint kick_tally; | |
19 | + long timerIdent; | |
20 | + | |
21 | + Dictionary<string, EntityPos> playerPositions; | |
22 | + | |
23 | + | |
24 | + public IdlePlayerControl(ICoreServerAPI _serverAPI) : base(_serverAPI) | |
25 | + { | |
26 | + playerPositions = new Dictionary<string, EntityPos>(32); | |
27 | + this.Command = "idlekick"; | |
28 | + this.Description = "Adjust Idle-ejector system settings."; | |
29 | + this.Syntax = @" set {time} | stats "; | |
30 | + this.RequiredPrivilege = Privilege.kick; | |
31 | + this.handler += HandleIdlerCommand; | |
32 | + ServerAPI.Event.PlayerLeave += PlayerLeft; | |
33 | + ServerAPI.Event.PlayerDisconnect += PlayerLeft; | |
34 | + | |
35 | + if (this.CachedConfiguration.IdleTimeout.TotalMinutes >= 5f) { | |
36 | + ServerAPI.Event.ServerRunPhase(EnumServerRunPhase.RunGame, RestartTimers); | |
37 | + } | |
38 | + | |
39 | + } | |
40 | + | |
41 | + private void HandleIdlerCommand(IServerPlayer player, int groupId, CmdArgs args) | |
42 | + { | |
43 | + var command = args.PopWord(string.Empty).ToLowerInvariant(); | |
44 | + | |
45 | + switch (command) { | |
46 | + case @"set": | |
47 | + //Change default & next cycle check time | |
48 | + var timeMinutes = args.PopInt(0); | |
49 | + if (timeMinutes >= 5) { | |
50 | + TimeSpan newTimeout = new TimeSpan(0, timeMinutes.Value, 0); | |
51 | + this.CachedConfiguration.IdleTimeout = newTimeout; | |
52 | + player.SendMessage(groupId, $"Changed Idle timeout to: {newTimeout}", EnumChatType.CommandSuccess); | |
53 | + RestartTimers(); | |
54 | + } else | |
55 | + player.SendMessage(groupId, "no change; Value must be >= 5", EnumChatType.CommandError); | |
56 | + break; | |
57 | + | |
58 | + case @"stats": | |
59 | + if (this.CachedConfiguration.IdleTimeout.TotalMinutes >= 5f) | |
60 | + { player.SendMessage(groupId, $"Ejected: {kick_tally} player(s), Timeout = {this.CachedConfiguration.IdleTimeout}", EnumChatType.CommandSuccess); } | |
61 | + else { player.SendMessage(groupId, "offline.", EnumChatType.CommandSuccess); } | |
62 | + break; | |
63 | + | |
64 | + default: | |
65 | + player.SendMessage(groupId, $"Unrecognised command: {command}", EnumChatType.CommandError); | |
66 | + break; | |
67 | + } | |
68 | + } | |
69 | + | |
70 | + private void RestartTimers( ) | |
71 | + { | |
72 | + if (timerIdent != 0L) | |
73 | + { | |
74 | + ServerAPI.Event.UnregisterCallback(timerIdent); | |
75 | + playerPositions.Clear( ); | |
76 | + FindIdlers(0f ); | |
77 | + } | |
78 | + timerIdent = ServerAPI.Event.RegisterCallback(FindIdlers, ( int )this.CachedConfiguration.IdleTimeout.TotalMilliseconds); | |
79 | + } | |
80 | + | |
81 | + private void FindIdlers(float delta ) | |
82 | + { | |
83 | + #if DEBUG | |
84 | + Logger.VerboseDebug("Starting idle player check"); | |
85 | + #endif | |
86 | + | |
87 | + foreach (var srvPlayer in ServerAPI.Server.Players.Where(plr => plr.ConnectionState == EnumClientState.Playing)) { | |
88 | + if (srvPlayer?.WorldData?.CurrentGameMode != EnumGameMode.Spectator) { | |
89 | + EntityPos oldPos; | |
90 | + if (playerPositions.TryGetValue(srvPlayer.PlayerUID, out oldPos)) { | |
91 | + if (oldPos.BasicallySameAsIgnoreAngles(srvPlayer.Entity.ServerPos, 0.001f)) { | |
92 | + if ( srvPlayer.Entity.CurrentControls.HasFlag( EnumEntityActivity.Idle) || | |
93 | + srvPlayer.Entity.CurrentControls.HasFlag(EnumEntityActivity.FloorSitting) || | |
94 | + srvPlayer.Entity.CurrentControls.HasFlag(EnumEntityActivity.Dead) || | |
95 | + srvPlayer.Entity.CurrentControls.HasFlag(EnumEntityActivity.None) ) | |
96 | + { | |
97 | + //Eee's not movin' | |
98 | + KickIdler(srvPlayer); | |
99 | + } | |
100 | + | |
101 | + } | |
102 | + } | |
103 | + else { | |
104 | + playerPositions.Add(srvPlayer.PlayerUID, srvPlayer.Entity.ServerPos); | |
105 | + } | |
106 | + } | |
107 | + } | |
108 | + } | |
109 | + | |
110 | + private void KickIdler(IServerPlayer them) | |
111 | + { | |
112 | + kick_tally++; | |
113 | + var message = MultiLang.Get(them.LanguageCode, idleKick_Key, them.PlayerName); | |
114 | + them.Disconnect(message); | |
115 | + ServerAPI.BroadcastMessageToAllGroups(message, EnumChatType.Notification); | |
116 | + } | |
117 | + | |
118 | + private void PlayerLeft(IServerPlayer byPlayer) | |
119 | + { | |
120 | + if (playerPositions.ContainsKey(byPlayer.PlayerUID)) playerPositions.Remove(byPlayer.PlayerUID); | |
121 | + } | |
122 | +} | |
123 | +} | |
124 | + |
@@ -10,4 +10,5 @@ | ||
10 | 10 | "atk:accepted-rules":"{0} HAS ACCEPTED THE RULES!", |
11 | 11 | "atk:promoted-to":"{0} have been promoted to: {1}", |
12 | 12 | "atk:demoted-to":"{0} have been demoted to: {1}", |
13 | + "atk:idle-kick":"{0} Ejected because of Idleness...", | |
13 | 14 | } |