Skip to content

Commit 8865016

Browse files
committed
add render-step events
1 parent 4b87801 commit 8865016

File tree

7 files changed

+148
-1
lines changed

7 files changed

+148
-1
lines changed

docs/release-notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
* For mod authors:
1414
* Updated to .NET 6.
15+
* Added `RenderingStep` and `RenderedStep` events, which let you handle a specific step in the game's render cycle.
1516
* Removed all deprecated APIs.
1617
* SMAPI no longer intercepts output written to the console. Mods which directly access `Console` will be listed under mod warnings.
1718

src/SMAPI/Events/IDisplayEvents.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ public interface IDisplayEvents
99
/// <summary>Raised after a game menu is opened, closed, or replaced.</summary>
1010
event EventHandler<MenuChangedEventArgs> MenuChanged;
1111

12+
/// <summary>Raised before the game draws a specific step in the rendering cycle.</summary>
13+
event EventHandler<RenderingStepEventArgs> RenderingStep;
14+
15+
/// <summary>Raised after the game draws a specific step in the rendering cycle.</summary>
16+
event EventHandler<RenderedStepEventArgs> RenderedStep;
17+
1218
/// <summary>Raised before the game draws anything to the screen in a draw tick, as soon as the sprite batch is opened. The sprite batch may be closed and reopened multiple times after this event is called, but it's only raised once per draw tick. This event isn't useful for drawing to the screen, since the game will draw over it.</summary>
1319
event EventHandler<RenderingEventArgs> Rendering;
1420

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.Xna.Framework.Graphics;
4+
using StardewValley;
5+
using StardewValley.Mods;
6+
7+
namespace StardewModdingAPI.Events
8+
{
9+
/// <summary>Event arguments for an <see cref="IDisplayEvents.RenderedStep"/> event.</summary>
10+
public class RenderedStepEventArgs : EventArgs
11+
{
12+
/*********
13+
** Fields
14+
*********/
15+
/// <summary>The cached instance for each render step.</summary>
16+
private static readonly Dictionary<RenderSteps, RenderedStepEventArgs> Instances = new();
17+
18+
19+
/*********
20+
** Accessors
21+
*********/
22+
/// <summary>The current step in the render cycle.</summary>
23+
public RenderSteps Step { get; }
24+
25+
/// <summary>The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.</summary>
26+
public SpriteBatch SpriteBatch => Game1.spriteBatch;
27+
28+
29+
/*********
30+
** Public methods
31+
*********/
32+
/// <summary>Construct an instance.</summary>
33+
/// <param name="step">The current step in the render cycle.</param>
34+
public RenderedStepEventArgs(RenderSteps step)
35+
{
36+
this.Step = step;
37+
}
38+
39+
/// <summary>Get an instance for a render step.</summary>
40+
/// <param name="step">The current step in the render cycle.</param>
41+
internal static RenderedStepEventArgs Instance(RenderSteps step)
42+
{
43+
if (!RenderedStepEventArgs.Instances.TryGetValue(step, out RenderedStepEventArgs instance))
44+
RenderedStepEventArgs.Instances[step] = instance = new(step);
45+
46+
return instance;
47+
}
48+
}
49+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.Xna.Framework.Graphics;
4+
using StardewValley;
5+
using StardewValley.Mods;
6+
7+
namespace StardewModdingAPI.Events
8+
{
9+
/// <summary>Event arguments for an <see cref="IDisplayEvents.RenderingStep"/> event.</summary>
10+
public class RenderingStepEventArgs : EventArgs
11+
{
12+
/*********
13+
** Fields
14+
*********/
15+
/// <summary>The cached instance for each render step.</summary>
16+
private static readonly Dictionary<RenderSteps, RenderingStepEventArgs> Instances = new();
17+
18+
19+
/*********
20+
** Accessors
21+
*********/
22+
/// <summary>The current step in the render cycle.</summary>
23+
public RenderSteps Step { get; }
24+
25+
/// <summary>The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.</summary>
26+
public SpriteBatch SpriteBatch => Game1.spriteBatch;
27+
28+
29+
/*********
30+
** Public methods
31+
*********/
32+
/// <summary>Construct an instance.</summary>
33+
/// <param name="step">The current step in the render cycle.</param>
34+
public RenderingStepEventArgs(RenderSteps step)
35+
{
36+
this.Step = step;
37+
}
38+
39+
/// <summary>Get an instance for a render step.</summary>
40+
/// <param name="step">The current step in the render cycle.</param>
41+
internal static RenderingStepEventArgs Instance(RenderSteps step)
42+
{
43+
if (!RenderingStepEventArgs.Instances.TryGetValue(step, out RenderingStepEventArgs instance))
44+
RenderingStepEventArgs.Instances[step] = instance = new(step);
45+
46+
return instance;
47+
}
48+
}
49+
}

src/SMAPI/Framework/Events/EventManager.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ internal class EventManager
3636
/// <inheritdoc cref="IDisplayEvents.Rendered" />
3737
public readonly ManagedEvent<RenderedEventArgs> Rendered;
3838

39+
/// <inheritdoc cref="IDisplayEvents.RenderingStep" />
40+
public readonly ManagedEvent<RenderingStepEventArgs> RenderingStep;
41+
42+
/// <inheritdoc cref="IDisplayEvents.RenderedStep" />
43+
public readonly ManagedEvent<RenderedStepEventArgs> RenderedStep;
44+
3945
/// <inheritdoc cref="IDisplayEvents.RenderingWorld" />
4046
public readonly ManagedEvent<RenderingWorldEventArgs> RenderingWorld;
4147

@@ -212,6 +218,8 @@ ManagedEvent<TEventArgs> ManageEventOf<TEventArgs>(string typeName, string event
212218
this.MenuChanged = ManageEventOf<MenuChangedEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.MenuChanged));
213219
this.Rendering = ManageEventOf<RenderingEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.Rendering));
214220
this.Rendered = ManageEventOf<RenderedEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.Rendered));
221+
this.RenderingStep = ManageEventOf<RenderingStepEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderingStep));
222+
this.RenderedStep = ManageEventOf<RenderedStepEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderedStep));
215223
this.RenderingWorld = ManageEventOf<RenderingWorldEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderingWorld));
216224
this.RenderedWorld = ManageEventOf<RenderedWorldEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderedWorld));
217225
this.RenderingActiveMenu = ManageEventOf<RenderingActiveMenuEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderingActiveMenu));

src/SMAPI/Framework/Events/ModDisplayEvents.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@ public event EventHandler<MenuChangedEventArgs> MenuChanged
1616
remove => this.EventManager.MenuChanged.Remove(value);
1717
}
1818

19+
/// <inheritdoc />
20+
public event EventHandler<RenderingStepEventArgs> RenderingStep
21+
{
22+
add => this.EventManager.RenderingStep.Add(value, this.Mod);
23+
remove => this.EventManager.RenderingStep.Remove(value);
24+
}
25+
26+
/// <inheritdoc />
27+
public event EventHandler<RenderedStepEventArgs> RenderedStep
28+
{
29+
add => this.EventManager.RenderedStep.Add(value, this.Mod);
30+
remove => this.EventManager.RenderedStep.Remove(value);
31+
}
32+
1933
/// <inheritdoc />
2034
public event EventHandler<RenderingEventArgs> Rendering
2135
{

src/SMAPI/Framework/SCore.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,10 @@ private void OnRenderingStep(RenderSteps step, SpriteBatch spriteBatch, RenderTa
12211221
this.RaiseRenderEvent(events.RenderingHud, spriteBatch, renderTarget);
12221222
break;
12231223
}
1224+
1225+
// raise generic rendering stage event
1226+
if (events.RenderingStep.HasListeners)
1227+
this.RaiseRenderEvent(events.RenderingStep, spriteBatch, renderTarget, RenderingStepEventArgs.Instance(step));
12241228
}
12251229

12261230
/// <summary>Raised when the game finishes a render step in the draw loop.</summary>
@@ -1245,6 +1249,10 @@ private void OnRenderedStep(RenderSteps step, SpriteBatch spriteBatch, RenderTar
12451249
this.RaiseRenderEvent(events.RenderedHud, spriteBatch, renderTarget);
12461250
break;
12471251
}
1252+
1253+
// raise generic rendering stage event
1254+
if (events.RenderedStep.HasListeners)
1255+
this.RaiseRenderEvent(events.RenderedStep, spriteBatch, renderTarget, RenderedStepEventArgs.Instance(step));
12481256
}
12491257

12501258
/// <summary>Raised after an instance finishes a draw loop.</summary>
@@ -1261,6 +1269,18 @@ private void OnRendered(RenderTarget2D renderTarget)
12611269
/// <param name="renderTarget">The render target being drawn to the screen.</param>
12621270
private void RaiseRenderEvent<TEventArgs>(ManagedEvent<TEventArgs> @event, SpriteBatch spriteBatch, RenderTarget2D renderTarget)
12631271
where TEventArgs : EventArgs, new()
1272+
{
1273+
this.RaiseRenderEvent(@event, spriteBatch, renderTarget, Singleton<TEventArgs>.Instance);
1274+
}
1275+
1276+
/// <summary>Raise a rendering/rendered event, temporarily opening the given sprite batch if needed to let mods draw to it.</summary>
1277+
/// <typeparam name="TEventArgs">The event args type to construct.</typeparam>
1278+
/// <param name="event">The event to raise.</param>
1279+
/// <param name="spriteBatch">The sprite batch being drawn to the screen.</param>
1280+
/// <param name="renderTarget">The render target being drawn to the screen.</param>
1281+
/// <param name="eventArgs">The event arguments to pass to the event.</param>
1282+
private void RaiseRenderEvent<TEventArgs>(ManagedEvent<TEventArgs> @event, SpriteBatch spriteBatch, RenderTarget2D renderTarget, TEventArgs eventArgs)
1283+
where TEventArgs : EventArgs
12641284
{
12651285
if (!@event.HasListeners)
12661286
return;
@@ -1286,7 +1306,7 @@ private void RaiseRenderEvent<TEventArgs>(ManagedEvent<TEventArgs> @event, Sprit
12861306
Game1.SetRenderTarget(renderTarget);
12871307
}
12881308

1289-
@event.RaiseEmpty();
1309+
@event.Raise(eventArgs);
12901310
}
12911311
finally
12921312
{

0 commit comments

Comments
 (0)