diff --git a/Examples/Intersect.Examples.ClientPlugin/Assets/join-our-discord.png b/Examples/Intersect.Examples.ClientPlugin/Assets/join-our-discord.png
new file mode 100644
index 0000000000..561c0c9e32
Binary files /dev/null and b/Examples/Intersect.Examples.ClientPlugin/Assets/join-our-discord.png differ
diff --git a/Examples/Intersect.Examples.ClientPlugin/ExampleClientPluginEntry.cs b/Examples/Intersect.Examples.ClientPlugin/ExampleClientPluginEntry.cs
new file mode 100644
index 0000000000..6620bed8b8
--- /dev/null
+++ b/Examples/Intersect.Examples.ClientPlugin/ExampleClientPluginEntry.cs
@@ -0,0 +1,141 @@
+using Intersect.Client.Framework.Content;
+using Intersect.Client.Framework.Graphics;
+using Intersect.Client.Framework.Gwen;
+using Intersect.Client.Framework.Gwen.Control;
+using Intersect.Client.Plugins;
+using Intersect.Client.Plugins.Interfaces;
+using Intersect.Plugins;
+using JetBrains.Annotations;
+using Microsoft;
+using System;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Threading;
+using Intersect.Client.General;
+using Intersect.Client.Interface;
+
+namespace Intersect.Examples.ClientPlugin
+{
+ ///
+ /// Demonstrates basic plugin functionality for the client.
+ ///
+ [SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters")]
+ public class ExampleClientPluginEntry : ClientPluginEntry
+ {
+ private bool mDisposed;
+ [UsedImplicitly] private Mutex mMutex;
+
+ private GameTexture mButtonTexture;
+
+ ///
+ public override void OnBootstrap([NotNull, ValidatedNotNull] IPluginBootstrapContext context)
+ {
+ context.Logging.Application.Info(
+ $@"{nameof(ExampleClientPluginEntry)}.{nameof(OnBootstrap)} writing to the application log!");
+
+ context.Logging.Plugin.Info(
+ $@"{nameof(ExampleClientPluginEntry)}.{nameof(OnBootstrap)} writing to the plugin log!");
+
+ mMutex = new Mutex(true, "testplugin", out var createdNew);
+ if (!createdNew)
+ {
+ Environment.Exit(-1);
+ }
+
+ var exampleCommandLineOptions = context.CommandLine.ParseArguments();
+ if (!exampleCommandLineOptions.ExampleFlag)
+ {
+ context.Logging.Plugin.Warn("Client wasn't started with the start-up flag!");
+ }
+
+ context.Logging.Plugin.Info(
+ $@"{nameof(exampleCommandLineOptions.ExampleVariable)} = {exampleCommandLineOptions.ExampleVariable}");
+ }
+
+ ///
+ public override void OnStart([NotNull, ValidatedNotNull] IClientPluginContext context)
+ {
+ context.Logging.Application.Info(
+ $@"{nameof(ExampleClientPluginEntry)}.{nameof(OnStart)} writing to the application log!");
+
+ context.Logging.Plugin.Info(
+ $@"{nameof(ExampleClientPluginEntry)}.{nameof(OnStart)} writing to the plugin log!");
+
+ mButtonTexture = context.ContentManager.LoadEmbedded(
+ context, ContentTypes.Interface, "Assets/join-our-discord.png");
+
+ context.Lifecycle.LifecycleChangeState += HandleLifecycleChangeState;
+ }
+
+ ///
+ public override void OnStop([NotNull, ValidatedNotNull] IClientPluginContext context)
+ {
+ context.Logging.Application.Info(
+ $@"{nameof(ExampleClientPluginEntry)}.{nameof(OnStop)} writing to the application log!");
+
+ context.Logging.Plugin.Info(
+ $@"{nameof(ExampleClientPluginEntry)}.{nameof(OnStop)} writing to the plugin log!");
+ }
+
+ private void HandleLifecycleChangeState([NotNull, ValidatedNotNull] IClientPluginContext context,
+ [NotNull, ValidatedNotNull] LifecycleChangeStateArgs lifecycleChangeStateArgs)
+ {
+ Debug.Assert(mButtonTexture != null, nameof(mButtonTexture) + " != null");
+
+ var activeInterface = context.Lifecycle.Interface;
+ if (activeInterface == null)
+ {
+ return;
+ }
+
+ switch (lifecycleChangeStateArgs.State)
+ {
+ case GameStates.Menu:
+ AddButtonToMainMenu(context, activeInterface);
+ break;
+ }
+ }
+
+ private void AddButtonToMainMenu([NotNull, ValidatedNotNull] IClientPluginContext context,
+ [NotNull, ValidatedNotNull] IMutableInterface activeInterface)
+ {
+ var button = activeInterface.Create