Add Discord Rich Presence

This commit is contained in:
teinarss
2020-03-25 19:44:14 +01:00
committed by abcdefg30
parent cae6c28754
commit c2026dc254
20 changed files with 342 additions and 11 deletions

View File

@@ -0,0 +1,201 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using DiscordRPC;
using DiscordRPC.Message;
using OpenRA.Network;
namespace OpenRA.Mods.Common
{
public enum DiscordState
{
Unknown,
InMenu,
InMapEditor,
InSkirmishLobby,
InMultiplayerLobby,
PlayingMultiplayer,
WatchingReplay,
PlayingCampaign,
PlayingSkirmish
}
public sealed class DiscordService : IGlobalModData, IDisposable
{
public readonly string ApplicationId = null;
readonly DiscordRpcClient client;
DiscordState currentState;
static readonly Lazy<DiscordService> Service = Exts.Lazy(() =>
{
if (!Game.ModData.Manifest.Contains<DiscordService>())
return null;
return Game.ModData.Manifest.Get<DiscordService>();
});
public DiscordService(MiniYaml yaml)
{
FieldLoader.Load(this, yaml);
// HACK: Prevent service from starting when launching the utility or server.
if (Game.Renderer == null)
return;
client = new DiscordRpcClient(ApplicationId, autoEvents: true)
{
SkipIdenticalPresence = false
};
client.OnJoin += OnJoin;
client.OnJoinRequested += OnJoinRequested;
// HACK: We need to set HasRegisteredUriScheme to bypass the check that is done when calling SetPresence with a joinSecret.
// DiscordRpc lib expect us to register uri handlers with RegisterUriScheme(), we are doing it ourselves in our installers/launchers.
client.GetType().GetProperty("HasRegisteredUriScheme").SetValue(client, true);
client.SetSubscription(EventType.Join | EventType.JoinRequest);
client.Initialize();
}
void OnJoinRequested(object sender, JoinRequestMessage args)
{
var client = (DiscordRpcClient)sender;
client.Respond(args, true);
}
void OnJoin(object sender, JoinMessage args)
{
if (currentState != DiscordState.InMenu)
return;
var server = args.Secret.Split('|');
Game.RunAfterTick(() =>
{
Game.RemoteDirectConnect(new ConnectionTarget(server[0], int.Parse(server[1])));
});
}
void SetStatus(DiscordState state, string details = null, string secret = null, int? players = null, int? slots = null)
{
if (currentState == state)
return;
if (Service.Value == null)
return;
string stateText;
DateTime? timestamp = null;
Party party = null;
Secrets secrets = null;
switch (state)
{
case DiscordState.InMenu:
stateText = "In menu";
break;
case DiscordState.InMapEditor:
stateText = "In Map Editor";
break;
case DiscordState.InSkirmishLobby:
stateText = "In Skirmish Lobby";
break;
case DiscordState.InMultiplayerLobby:
stateText = "In Multiplayer Lobby";
timestamp = DateTime.UtcNow;
party = new Party
{
ID = Secrets.CreateFriendlySecret(new Random()),
Size = players.Value,
Max = slots.Value
};
secrets = new Secrets
{
JoinSecret = secret
};
break;
case DiscordState.PlayingMultiplayer:
stateText = "Playing Multiplayer";
timestamp = DateTime.UtcNow;
break;
case DiscordState.PlayingCampaign:
stateText = "Playing Campaign";
timestamp = DateTime.UtcNow;
break;
case DiscordState.WatchingReplay:
stateText = "Watching Replay";
timestamp = DateTime.UtcNow;
break;
case DiscordState.PlayingSkirmish:
stateText = "Playing Skirmish";
timestamp = DateTime.UtcNow;
break;
default:
throw new ArgumentOutOfRangeException("state", state, null);
}
var richPresence = new RichPresence
{
Details = details,
State = stateText,
Assets = new Assets
{
LargeImageKey = "large",
LargeImageText = Game.ModData.Manifest.Metadata.Title,
},
Timestamps = timestamp.HasValue ? new Timestamps(timestamp.Value) : null,
Party = party,
Secrets = secrets
};
client.SetPresence(richPresence);
currentState = state;
}
public void Dispose()
{
if (client != null)
client.Dispose();
}
public static void UpdateStatus(DiscordState state, string details = null, string secret = null, int? players = null, int? slots = null)
{
if (Service.Value != null)
Service.Value.SetStatus(state, details, secret, players, slots);
}
public static void SetPlayers(int players, int slots)
{
if (Service.Value != null)
{
Service.Value.client.UpdateParty(new Party
{
ID = Secrets.CreateFriendlySecret(new Random()),
Size = players,
Max = slots
});
}
}
public static void UpdatePlayers(int players, int slots)
{
if (Service.Value != null)
Service.Value.client.UpdatePartySize(players, slots);
}
public static void UpdateDetails(string details)
{
if (Service.Value != null)
Service.Value.client.UpdateDetails(details);
}
}
}

View File

@@ -25,6 +25,7 @@
<Private>False</Private>
</ProjectReference>
<PackageReference Include="OpenRA-FuzzyLogicLibrary" Version="1.0.1" />
<PackageReference Include="DiscordRichPresence" Version="1.0.150" />
<PackageReference Include="rix0rrr.BeaconLib" Version="1.0.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />

View File

@@ -61,6 +61,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
bool addBotOnMapLoad;
bool disableTeamChat;
bool teamChat;
bool updateDiscordStatus = true;
readonly string chatLineSound = ChromeMetrics.Get<string>("ChatLineSound");
@@ -116,6 +117,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
orderManager.AddChatLine += AddChatLine;
Game.LobbyInfoChanged += UpdateCurrentMap;
Game.LobbyInfoChanged += UpdatePlayerList;
Game.LobbyInfoChanged += UpdateDiscordStatus;
Game.BeforeGameStart += OnGameStart;
Game.ConnectionStateChanged += ConnectionStateChanged;
@@ -462,6 +464,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
orderManager.AddChatLine -= AddChatLine;
Game.LobbyInfoChanged -= UpdateCurrentMap;
Game.LobbyInfoChanged -= UpdatePlayerList;
Game.LobbyInfoChanged -= UpdateDiscordStatus;
Game.BeforeGameStart -= OnGameStart;
Game.ConnectionStateChanged -= ConnectionStateChanged;
}
@@ -743,9 +746,56 @@ namespace OpenRA.Mods.Common.Widgets.Logic
tabCompletion.Names = orderManager.LobbyInfo.Clients.Select(c => c.Name).Distinct().ToList();
}
void UpdateDiscordStatus()
{
var mapTitle = map.Title;
var numberOfPlayers = 0;
var slots = 0;
if (!skirmishMode)
{
foreach (var kv in orderManager.LobbyInfo.Slots)
{
if (kv.Value.Closed)
continue;
slots++;
var client = orderManager.LobbyInfo.ClientInSlot(kv.Key);
if (client != null)
numberOfPlayers++;
}
}
if (updateDiscordStatus)
{
string secret = null;
if (orderManager.LobbyInfo.GlobalSettings.Dedicated)
{
var endpoint = orderManager.Endpoint.GetConnectEndPoints().First();
secret = string.Concat(endpoint.Address, "|", endpoint.Port);
}
var state = skirmishMode ? DiscordState.InSkirmishLobby : DiscordState.InMultiplayerLobby;
DiscordService.UpdateStatus(state, mapTitle, secret, numberOfPlayers, slots);
updateDiscordStatus = false;
}
else
{
if (!skirmishMode)
DiscordService.UpdatePlayers(numberOfPlayers, slots);
DiscordService.UpdateDetails(mapTitle);
}
}
void OnGameStart()
{
Ui.CloseWindow();
var state = skirmishMode ? DiscordState.PlayingSkirmish : DiscordState.PlayingMultiplayer;
DiscordService.UpdateStatus(state);
onStart();
}
}

View File

@@ -46,6 +46,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
menuType = type;
DiscordService.UpdateStatus(DiscordState.InMenu);
// Update button mouseover
Game.RunAfterTick(Ui.ResetTooltips);
}
@@ -258,6 +260,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
onIntroductionComplete();
Game.OnShellmapLoaded += OpenMenuBasedOnLastGame;
DiscordService.UpdateStatus(DiscordState.InMenu);
}
void LoadAndDisplayNews(string newsURL, Widget newsBG)
@@ -317,6 +321,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
() => { Game.LoadEditor(uid); },
() => { Game.CloseServer(); SwitchMenu(MenuType.MapEditor); });
DiscordService.UpdateStatus(DiscordState.InMapEditor);
lastGameState = MenuPanel.MapEditor;
}

View File

@@ -164,6 +164,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
void OnGameStart()
{
Ui.CloseWindow();
DiscordService.UpdateStatus(DiscordState.PlayingCampaign);
onStart();
}

View File

@@ -95,6 +95,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
});
Game.Disconnect();
DiscordService.UpdateStatus(DiscordState.InMenu);
};
Game.OpenWindow("SERVER_LOBBY", new WidgetArgs

View File

@@ -688,6 +688,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (selectedReplay != null && ReplayUtils.PromptConfirmReplayCompatibility(selectedReplay))
{
cancelLoadingReplays = true;
DiscordService.UpdateStatus(DiscordState.WatchingReplay);
Game.JoinReplay(selectedReplay.FilePath);
}
}