Skip to content

Commit fffc58e

Browse files
committed
reduce allocations for active button state tracking
1 parent 2aeaf6f commit fffc58e

File tree

1 file changed

+36
-40
lines changed

1 file changed

+36
-40
lines changed

src/SMAPI/Framework/Input/SInputState.cs

Lines changed: 36 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ internal sealed class SInputState : InputState
5454
/// <summary>The mouse state as of the last update, with overrides applied.</summary>
5555
public MouseState MouseState { get; private set; }
5656

57-
/// <summary>The buttons which were pressed, held, or released as of the last update.</summary>
58-
public IDictionary<SButton, SButtonState> ButtonStates { get; private set; } = new Dictionary<SButton, SButtonState>();
57+
/// <summary>The buttons which were active (pressed, held, or newly released) as of the last update.</summary>
58+
/// <remarks>A released button is considered inactive if it's been released for two consecutive ticks, at which point it's no longer in this dictionary.</remarks>
59+
public Dictionary<SButton, SButtonState> ButtonStates { get; } = [];
5960

6061
/// <summary>The cursor position on the screen adjusted for the zoom level.</summary>
6162
public ICursorPosition CursorPosition => this.CursorPositionImpl;
@@ -96,10 +97,9 @@ public void TrueUpdate()
9697
Vector2? playerTilePos = Context.IsPlayerFree ? Game1.player.Tile : null;
9798

9899
pressedButtons.Clear();
99-
this.FillPressedButtons(pressedButtons, keyboard, mouse, controller);
100+
SInputState.FillPressedButtons(pressedButtons, keyboard, mouse, controller);
100101

101102
// apply overrides
102-
bool hasOverrides = false;
103103
if (this.CustomPressedKeys.Count > 0 || this.CustomReleasedKeys.Count > 0)
104104
{
105105
// reset overrides that no longer apply
@@ -108,31 +108,24 @@ public void TrueUpdate()
108108

109109
// apply overrides
110110
if (this.ApplyOverrides(this.CustomPressedKeys, this.CustomReleasedKeys, controller, keyboard, mouse))
111-
hasOverrides = true;
112-
113-
// remove pressed keys
111+
{
112+
pressedButtons.Clear();
113+
SInputState.FillPressedButtons(pressedButtons, keyboard, mouse, controller);
114+
}
114115
this.CustomPressedKeys.Clear();
115116
}
116117

117-
// get button states
118-
if (hasOverrides)
119-
{
120-
pressedButtons.Clear();
121-
this.FillPressedButtons(pressedButtons, keyboard, mouse, controller);
122-
}
123-
var activeButtons = this.DeriveStates(this.ButtonStates, pressedButtons);
124-
125118
// update
126119
this.HasNewOverrides = false;
127120
this.ControllerState = controller.GetState();
128121
this.KeyboardState = keyboard.GetState();
129122
this.MouseState = mouse.GetState();
130-
this.ButtonStates = activeButtons;
131123
if (cursorAbsolutePos != this.CursorPositionImpl.AbsolutePixels || playerTilePos != this.LastPlayerTile)
132124
{
133125
this.LastPlayerTile = playerTilePos;
134126
this.CursorPositionImpl = this.GetCursorPosition(this.MouseState, cursorAbsolutePos, zoomMultiplier);
135127
}
128+
SInputState.UpdateActiveStates(this.ButtonStates, pressedButtons);
136129
}
137130
catch (InvalidOperationException)
138131
{
@@ -204,7 +197,7 @@ public void ApplyOverrides()
204197
/// <param name="button">The button to check.</param>
205198
public bool IsDown(SButton button)
206199
{
207-
return this.GetState(this.ButtonStates, button).IsDown();
200+
return SInputState.GetState(this.ButtonStates, button).IsDown();
208201
}
209202

210203
/// <summary>Get whether any of the given buttons were pressed or held.</summary>
@@ -218,7 +211,7 @@ public bool IsAnyDown(InputButton[] buttons)
218211
/// <param name="button">The button to check.</param>
219212
public SButtonState GetState(SButton button)
220213
{
221-
return this.GetState(this.ButtonStates, button);
214+
return SInputState.GetState(this.ButtonStates, button);
222215
}
223216

224217

@@ -255,9 +248,9 @@ private bool ApplyOverrides(ISet<SButton> pressed, ISet<SButton> released, GameP
255248
IDictionary<SButton, SButtonState> keyboardOverrides = new Dictionary<SButton, SButtonState>();
256249
IDictionary<SButton, SButtonState> controllerOverrides = new Dictionary<SButton, SButtonState>();
257250
IDictionary<SButton, SButtonState> mouseOverrides = new Dictionary<SButton, SButtonState>();
258-
foreach (var button in pressed.Concat(released))
251+
foreach (SButton button in pressed.Concat(released))
259252
{
260-
var newState = this.DeriveState(
253+
SButtonState newState = SInputState.DeriveState(
261254
oldState: this.GetState(button),
262255
isDown: pressed.Contains(button)
263256
);
@@ -281,43 +274,46 @@ private bool ApplyOverrides(ISet<SButton> pressed, ISet<SButton> released, GameP
281274
return true;
282275
}
283276

284-
/// <summary>Get the state of all pressed or released buttons relative to their previous state.</summary>
285-
/// <param name="previousStates">The previous button states.</param>
277+
/// <summary>Update active button states based on the currently pressed buttons.</summary>
278+
/// <param name="buttonStates">The button states from the previous update tick.</param>
286279
/// <param name="pressedButtons">The currently pressed buttons.</param>
287-
private IDictionary<SButton, SButtonState> DeriveStates(IDictionary<SButton, SButtonState> previousStates, HashSet<SButton> pressedButtons)
280+
private static void UpdateActiveStates(Dictionary<SButton, SButtonState> buttonStates, HashSet<SButton> pressedButtons)
288281
{
289-
IDictionary<SButton, SButtonState> activeButtons = new Dictionary<SButton, SButtonState>();
290-
291-
// handle pressed keys
292-
foreach (SButton button in pressedButtons)
293-
activeButtons[button] = this.DeriveState(this.GetState(previousStates, button), isDown: true);
294-
295-
// handle released keys
296-
foreach (KeyValuePair<SButton, SButtonState> prev in previousStates)
282+
// update previously active keys
283+
foreach ((SButton button, SButtonState oldState) in buttonStates)
297284
{
298-
if (prev.Value.IsDown())
299-
activeButtons.TryAdd(prev.Key, SButtonState.Released);
285+
SButtonState newState = SInputState.DeriveState(oldState, isDown: pressedButtons.Contains(button));
286+
287+
if (oldState == SButtonState.Released && newState == SButtonState.Released)
288+
buttonStates.Remove(button);
289+
else if (oldState != newState)
290+
buttonStates[button] = newState;
300291
}
301292

302-
return activeButtons;
293+
// add newly pressed keys
294+
foreach (SButton button in pressedButtons)
295+
buttonStates.TryAdd(button, SButtonState.Pressed);
303296
}
304297

305298
/// <summary>Get the state of a button relative to its previous state.</summary>
306299
/// <param name="oldState">The previous button state.</param>
307300
/// <param name="isDown">Whether the button is currently down.</param>
308-
private SButtonState DeriveState(SButtonState oldState, bool isDown)
301+
private static SButtonState DeriveState(SButtonState oldState, bool isDown)
309302
{
310-
if (isDown && oldState.IsDown())
311-
return SButtonState.Held;
312303
if (isDown)
313-
return SButtonState.Pressed;
304+
{
305+
return oldState.IsDown()
306+
? SButtonState.Held
307+
: SButtonState.Pressed;
308+
}
309+
314310
return SButtonState.Released;
315311
}
316312

317313
/// <summary>Get the state of a button.</summary>
318314
/// <param name="activeButtons">The current button states to check.</param>
319315
/// <param name="button">The button to check.</param>
320-
private SButtonState GetState(IDictionary<SButton, SButtonState> activeButtons, SButton button)
316+
private static SButtonState GetState(IDictionary<SButton, SButtonState> activeButtons, SButton button)
321317
{
322318
return activeButtons.TryGetValue(button, out SButtonState state)
323319
? state
@@ -330,7 +326,7 @@ private SButtonState GetState(IDictionary<SButton, SButtonState> activeButtons,
330326
/// <param name="mouse">The mouse state.</param>
331327
/// <param name="controller">The controller state.</param>
332328
/// <remarks>Thumbstick direction logic derived from <see cref="ButtonCollection"/>.</remarks>
333-
private void FillPressedButtons(HashSet<SButton> set, KeyboardStateBuilder keyboard, MouseStateBuilder mouse, GamePadStateBuilder controller)
329+
private static void FillPressedButtons(HashSet<SButton> set, KeyboardStateBuilder keyboard, MouseStateBuilder mouse, GamePadStateBuilder controller)
334330
{
335331
keyboard.FillPressedButtons(set);
336332
mouse.FillPressedButtons(set);

0 commit comments

Comments
 (0)