10 |
11 |
12 |
13 |
14 | This package provides a wrapper for lachee/discord-rpc-csharp and
15 | a better experience when intergrating with Unity3D, as well as solving some tricky annoyances such as named pipes and mono.
16 |
17 |
18 |
19 |
20 |
21 | # Usage
22 | Add the package to your project and look at the sample code. For more documentation about the RPC, check the
23 | [discord-rpc-csharp](https://github.com/lachee/discord-rpc-csharp) documentation
24 |
25 | Check out the documentation at [https://lachee.github.io/discord-rpc-unity/](https://lachee.github.io/discord-rpc-unity/)
26 |
27 | # Dependencies
28 |
29 | - At _least_ Unity 2018, however:
30 | - Support is **only given down to** [Unity 2018.4.36f1](https://unity3d.com/unity/qa/lts-releases?version=2018.4) LTS
31 | - Support is **only given up to** the latest LTS
32 |
33 | - Newtonsoft.JSON 13
34 | - This is provided by [com.unity.nuget.newtonsoft-json](https://docs.unity3d.com/Packages/com.unity.nuget.newtonsoft-json@3.0/manual/index.html)
35 | - _this determines the 2018.4 min spec. You can go lower, but you need to supply your own newtonsoft.json 13.0 binary._
36 |
37 |
38 | # Installation
39 | #### OpenUPM
40 | The [openupm registry](https://openupm.com) is a open source package manager for Unity and provides the [openupm-cli](https://github.com/openupm/openupm-cli) to manage your dependencies.
41 | ```
42 | openupm add com.lachee.discordrpc
43 | ```
44 |
45 | #### Manual UPM
46 | Use the Unity Package Manager to add a git package. Adding the git to your UPM will limit updates as Unity will not track versioning on git projects (even though they totally could with tags).
47 | 1. Open the Unity Package Manager and `Add Package by git URL...`
48 | 2. `https://github.com/Lachee/discord-rpc-unity.git `
49 |
50 | For local editable versions, manually clone the repo into your package folder. Note the exact spelling on destination name.
51 | 1. `git clone https://github.com/Lachee/discord-rpc-unity.git Packages/com.lachee.discordrpc`
52 |
53 | #### Unity Package
54 | Go old school and download the Unity Package and import it into your project.
55 | 1. Download the `.unitypackage` from the [Releases](releases) or via the last run `Create Release` action.
56 | 2. Import that package into your Unity3D
57 |
58 | # Logging
59 |
60 | By default, the DiscordManager will log to the Unity Console while in the Editor.
61 | To enable logging in builds, create a [Development Build](https://docs.unity3d.com/Manual/BuildSettings.html) and a new discordrpc.log file will be generated with your app when it runs.
62 |
63 | # Licensing
64 |
65 | The license is MIT so do what you want;
66 |
67 | However, i do appriciate attributations where possible and a link. Also if you plan to "fix" the library and sell it, please contribute back to this project with your fixes so others can benifit too.
68 |
--------------------------------------------------------------------------------
/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ed0c046e615465941bb83ddda6d4c83a
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Resources.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 6717e8f35d614e847ac2e1c8d65ac82c
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Resources/discord_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lachee/discord-rpc-unity/7498674d0192bb8bb68f417c13b57884b50144e3/Resources/discord_logo.png
--------------------------------------------------------------------------------
/Resources/discord_logo.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0e82c8e5c734e424d827caf275b12cb9
3 | TextureImporter:
4 | fileIDToRecycleName:
5 | 2186277476908879412: ImportLogs
6 | externalObjects: {}
7 | serializedVersion: 5
8 | mipmaps:
9 | mipMapMode: 0
10 | enableMipMap: 0
11 | sRGBTexture: 1
12 | linearTexture: 0
13 | fadeOut: 0
14 | borderMipMap: 0
15 | mipMapsPreserveCoverage: 0
16 | alphaTestReferenceValue: 0.5
17 | mipMapFadeDistanceStart: 1
18 | mipMapFadeDistanceEnd: 3
19 | bumpmap:
20 | convertToNormalMap: 0
21 | externalNormalMap: 0
22 | heightScale: 0.25
23 | normalMapFilter: 0
24 | isReadable: 0
25 | grayScaleToAlpha: 0
26 | generateCubemap: 6
27 | cubemapConvolution: 0
28 | seamlessCubemap: 0
29 | textureFormat: 1
30 | maxTextureSize: 2048
31 | textureSettings:
32 | serializedVersion: 2
33 | filterMode: 2
34 | aniso: -1
35 | mipBias: -1
36 | wrapU: 1
37 | wrapV: 1
38 | wrapW: -1
39 | nPOTScale: 0
40 | lightmap: 0
41 | compressionQuality: 50
42 | spriteMode: 1
43 | spriteExtrude: 1
44 | spriteMeshType: 1
45 | alignment: 0
46 | spritePivot: {x: 0.5, y: 0.5}
47 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
48 | spritePixelsToUnits: 100
49 | alphaUsage: 1
50 | alphaIsTransparency: 1
51 | spriteTessellationDetail: -1
52 | textureType: 8
53 | textureShape: 1
54 | singleChannelComponent: 0
55 | maxTextureSizeSet: 0
56 | compressionQualitySet: 0
57 | textureFormatSet: 0
58 | platformSettings:
59 | - serializedVersion: 2
60 | buildTarget: DefaultTexturePlatform
61 | maxTextureSize: 2048
62 | resizeAlgorithm: 0
63 | textureFormat: -1
64 | textureCompression: 0
65 | compressionQuality: 50
66 | crunchedCompression: 0
67 | allowsAlphaSplitting: 0
68 | overridden: 0
69 | androidETC2FallbackOverride: 0
70 | - serializedVersion: 2
71 | buildTarget: Standalone
72 | maxTextureSize: 2048
73 | resizeAlgorithm: 0
74 | textureFormat: -1
75 | textureCompression: 0
76 | compressionQuality: 50
77 | crunchedCompression: 0
78 | allowsAlphaSplitting: 0
79 | overridden: 0
80 | androidETC2FallbackOverride: 0
81 | spriteSheet:
82 | serializedVersion: 2
83 | sprites: []
84 | outline: []
85 | physicsShape: []
86 | bones: []
87 | spriteID: cf9d870bf415949439b4a4907ce72e6f
88 | vertices: []
89 | indices:
90 | edges: []
91 | weights: []
92 | spritePackingTag:
93 | userData:
94 | assetBundleName:
95 | assetBundleVariant:
96 |
--------------------------------------------------------------------------------
/Resources/discord_presence.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lachee/discord-rpc-unity/7498674d0192bb8bb68f417c13b57884b50144e3/Resources/discord_presence.png
--------------------------------------------------------------------------------
/Resources/discord_presence.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 776a7bd6dde298048840a64474480461
3 | timeCreated: 1521361092
4 | licenseType: Free
5 | TextureImporter:
6 | fileIDToRecycleName: {}
7 | serializedVersion: 4
8 | mipmaps:
9 | mipMapMode: 0
10 | enableMipMap: 0
11 | sRGBTexture: 0
12 | linearTexture: 0
13 | fadeOut: 0
14 | borderMipMap: 0
15 | mipMapFadeDistanceStart: 1
16 | mipMapFadeDistanceEnd: 3
17 | bumpmap:
18 | convertToNormalMap: 0
19 | externalNormalMap: 0
20 | heightScale: 0.25
21 | normalMapFilter: 0
22 | isReadable: 0
23 | grayScaleToAlpha: 0
24 | generateCubemap: 6
25 | cubemapConvolution: 0
26 | seamlessCubemap: 0
27 | textureFormat: 1
28 | maxTextureSize: 2048
29 | textureSettings:
30 | filterMode: -1
31 | aniso: 1
32 | mipBias: -1
33 | wrapMode: 1
34 | nPOTScale: 0
35 | lightmap: 0
36 | compressionQuality: 50
37 | spriteMode: 0
38 | spriteExtrude: 1
39 | spriteMeshType: 1
40 | alignment: 0
41 | spritePivot: {x: 0.5, y: 0.5}
42 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
43 | spritePixelsToUnits: 100
44 | alphaUsage: 1
45 | alphaIsTransparency: 1
46 | spriteTessellationDetail: -1
47 | textureType: 2
48 | textureShape: 1
49 | maxTextureSizeSet: 0
50 | compressionQualitySet: 0
51 | textureFormatSet: 0
52 | platformSettings:
53 | - buildTarget: DefaultTexturePlatform
54 | maxTextureSize: 2048
55 | textureFormat: -1
56 | textureCompression: 1
57 | compressionQuality: 50
58 | crunchedCompression: 0
59 | allowsAlphaSplitting: 0
60 | overridden: 0
61 | - buildTarget: Standalone
62 | maxTextureSize: 2048
63 | textureFormat: -1
64 | textureCompression: 1
65 | compressionQuality: 50
66 | crunchedCompression: 0
67 | allowsAlphaSplitting: 0
68 | overridden: 0
69 | spriteSheet:
70 | serializedVersion: 2
71 | sprites: []
72 | outline: []
73 | spritePackingTag:
74 | userData:
75 | assetBundleName:
76 | assetBundleVariant:
77 |
--------------------------------------------------------------------------------
/Resources/discord_wumpus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lachee/discord-rpc-unity/7498674d0192bb8bb68f417c13b57884b50144e3/Resources/discord_wumpus.png
--------------------------------------------------------------------------------
/Resources/discord_wumpus.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8e8e47700789ac34c9e419fd91fb84ff
3 | TextureImporter:
4 | fileIDToRecycleName:
5 | 2186277476908879412: ImportLogs
6 | externalObjects: {}
7 | serializedVersion: 5
8 | mipmaps:
9 | mipMapMode: 0
10 | enableMipMap: 0
11 | sRGBTexture: 1
12 | linearTexture: 0
13 | fadeOut: 0
14 | borderMipMap: 0
15 | mipMapsPreserveCoverage: 0
16 | alphaTestReferenceValue: 0.5
17 | mipMapFadeDistanceStart: 1
18 | mipMapFadeDistanceEnd: 3
19 | bumpmap:
20 | convertToNormalMap: 0
21 | externalNormalMap: 0
22 | heightScale: 0.25
23 | normalMapFilter: 0
24 | isReadable: 0
25 | grayScaleToAlpha: 0
26 | generateCubemap: 6
27 | cubemapConvolution: 0
28 | seamlessCubemap: 0
29 | textureFormat: 1
30 | maxTextureSize: 2048
31 | textureSettings:
32 | serializedVersion: 2
33 | filterMode: 2
34 | aniso: -1
35 | mipBias: -1
36 | wrapU: 1
37 | wrapV: 1
38 | wrapW: -1
39 | nPOTScale: 0
40 | lightmap: 0
41 | compressionQuality: 50
42 | spriteMode: 1
43 | spriteExtrude: 9
44 | spriteMeshType: 1
45 | alignment: 0
46 | spritePivot: {x: 0.5, y: 0.5}
47 | spritePixelsToUnits: 100
48 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
49 | spriteGenerateFallbackPhysicsShape: 1
50 | alphaUsage: 2
51 | alphaIsTransparency: 1
52 | spriteTessellationDetail: -1
53 | textureType: 8
54 | textureShape: 1
55 | singleChannelComponent: 0
56 | maxTextureSizeSet: 0
57 | compressionQualitySet: 0
58 | textureFormatSet: 0
59 | platformSettings:
60 | - serializedVersion: 2
61 | buildTarget: DefaultTexturePlatform
62 | maxTextureSize: 2048
63 | resizeAlgorithm: 1
64 | textureFormat: -1
65 | textureCompression: 0
66 | compressionQuality: 50
67 | crunchedCompression: 0
68 | allowsAlphaSplitting: 0
69 | overridden: 0
70 | androidETC2FallbackOverride: 0
71 | - serializedVersion: 2
72 | buildTarget: Standalone
73 | maxTextureSize: 2048
74 | resizeAlgorithm: 1
75 | textureFormat: -1
76 | textureCompression: 0
77 | compressionQuality: 50
78 | crunchedCompression: 0
79 | allowsAlphaSplitting: 0
80 | overridden: 0
81 | androidETC2FallbackOverride: 0
82 | spriteSheet:
83 | serializedVersion: 2
84 | sprites: []
85 | outline: []
86 | physicsShape: []
87 | bones: []
88 | spriteID: 9ad9a18e0fe29454293a7fa245ecd109
89 | vertices: []
90 | indices:
91 | edges: []
92 | weights: []
93 | spritePackingTag:
94 | userData:
95 | assetBundleName:
96 | assetBundleVariant:
97 |
--------------------------------------------------------------------------------
/Resources/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lachee/discord-rpc-unity/7498674d0192bb8bb68f417c13b57884b50144e3/Resources/logo.png
--------------------------------------------------------------------------------
/Resources/logo.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7d5afae97af0a5c428d09c1e51cfa04c
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 1
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | vTOnly: 0
27 | ignoreMasterTextureLimit: 0
28 | grayScaleToAlpha: 0
29 | generateCubemap: 6
30 | cubemapConvolution: 0
31 | seamlessCubemap: 0
32 | textureFormat: 1
33 | maxTextureSize: 2048
34 | textureSettings:
35 | serializedVersion: 2
36 | filterMode: 1
37 | aniso: 1
38 | mipBias: 0
39 | wrapU: 0
40 | wrapV: 0
41 | wrapW: 0
42 | nPOTScale: 1
43 | lightmap: 0
44 | compressionQuality: 50
45 | spriteMode: 0
46 | spriteExtrude: 1
47 | spriteMeshType: 1
48 | alignment: 0
49 | spritePivot: {x: 0.5, y: 0.5}
50 | spritePixelsToUnits: 100
51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
52 | spriteGenerateFallbackPhysicsShape: 1
53 | alphaUsage: 1
54 | alphaIsTransparency: 0
55 | spriteTessellationDetail: -1
56 | textureType: 0
57 | textureShape: 1
58 | singleChannelComponent: 0
59 | flipbookRows: 1
60 | flipbookColumns: 1
61 | maxTextureSizeSet: 0
62 | compressionQualitySet: 0
63 | textureFormatSet: 0
64 | ignorePngGamma: 0
65 | applyGammaDecoding: 0
66 | platformSettings:
67 | - serializedVersion: 3
68 | buildTarget: DefaultTexturePlatform
69 | maxTextureSize: 2048
70 | resizeAlgorithm: 0
71 | textureFormat: -1
72 | textureCompression: 1
73 | compressionQuality: 50
74 | crunchedCompression: 0
75 | allowsAlphaSplitting: 0
76 | overridden: 0
77 | androidETC2FallbackOverride: 0
78 | forceMaximumCompressionQuality_BC6H_BC7: 0
79 | spriteSheet:
80 | serializedVersion: 2
81 | sprites: []
82 | outline: []
83 | physicsShape: []
84 | bones: []
85 | spriteID:
86 | internalID: 0
87 | vertices: []
88 | indices:
89 | edges: []
90 | weights: []
91 | secondaryTextures: []
92 | nameFileIdTable: {}
93 | spritePackingTag:
94 | pSDRemoveMatte: 0
95 | pSDShowRemoveMatteOption: 0
96 | userData:
97 | assetBundleName:
98 | assetBundleVariant:
99 |
--------------------------------------------------------------------------------
/Runtime.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d2cb646afe6b9434d98cab3fc62ff4ec
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/Attributes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: eee91a11d8111e542912260226300ee0
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/Attributes/CharacterLimitAttribute.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace Lachee.Discord.Attributes
4 | {
5 | public class CharacterLimitAttribute : PropertyAttribute
6 | {
7 | public int max = 32;
8 | public bool enforce = false;
9 |
10 | public CharacterLimitAttribute(int max)
11 | {
12 | this.max = max;
13 | this.enforce = false;
14 | }
15 |
16 | public CharacterLimitAttribute(int max, bool enforce)
17 | {
18 | this.max = max;
19 | this.enforce = enforce;
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/Runtime/Attributes/CharacterLimitAttribute.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f5817f51f41457e479f8fc1e43bf363f
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/Control.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e952ad0a6c46a6d4e98f7e3c2000c337
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/Control/MessageEvents.cs:
--------------------------------------------------------------------------------
1 | using DiscordRPC;
2 | using DiscordRPC.Message;
3 | using System;
4 | using UnityEngine;
5 | using UnityEngine.Events;
6 |
7 | namespace Lachee.Discord.Control
8 | {
9 | [Serializable]
10 | public sealed class MessageEvents
11 | {
12 | [Serializable]
13 | public sealed class ReadyMessageEvent : UnityEvent { }
14 |
15 | [Serializable]
16 | public sealed class CloseMessageEvent : UnityEvent { }
17 |
18 | [Serializable]
19 | public sealed class ErrorMessageEvent : UnityEvent { }
20 |
21 | [Serializable]
22 | public sealed class PresenceMessageEvent : UnityEvent { }
23 |
24 | [Serializable]
25 | public sealed class SubscribeMessageEvent : UnityEvent { }
26 |
27 | [Serializable]
28 | public sealed class UnsubscribeMessageEvent : UnityEvent { }
29 |
30 | [Serializable]
31 | public sealed class JoinMessageEvent : UnityEvent { }
32 |
33 | [Serializable]
34 | public sealed class SpectateMessageEvent : UnityEvent { }
35 |
36 | [Serializable]
37 | public sealed class JoinRequestMessageEvent : UnityEvent { }
38 |
39 | [Serializable]
40 | public sealed class ConnectionEstablishedMessageEvent : UnityEvent { }
41 |
42 | [Serializable]
43 | public sealed class ConnectionFailedMessageEvent : UnityEvent { }
44 |
45 | public ReadyMessageEvent OnReady = new ReadyMessageEvent();
46 | public CloseMessageEvent OnClose = new CloseMessageEvent();
47 | public ErrorMessageEvent OnError = new ErrorMessageEvent();
48 | public PresenceMessageEvent OnPresenceUpdate = new PresenceMessageEvent();
49 | public SubscribeMessageEvent OnSubscribe = new SubscribeMessageEvent();
50 | public UnsubscribeMessageEvent OnUnsubscribe = new UnsubscribeMessageEvent();
51 | public JoinMessageEvent OnJoin = new JoinMessageEvent();
52 | public SpectateMessageEvent OnSpectate = new SpectateMessageEvent();
53 | public JoinRequestMessageEvent OnJoinRequest = new JoinRequestMessageEvent();
54 | public ConnectionEstablishedMessageEvent OnConnectionEstablished = new ConnectionEstablishedMessageEvent();
55 | public ConnectionFailedMessageEvent OnConnectionFailed = new ConnectionFailedMessageEvent();
56 |
57 | public void RegisterEvents(DiscordRpcClient client)
58 | {
59 | client.OnReady += (s, args) => OnReady.Invoke(args);
60 | client.OnClose += (s, args) => OnClose.Invoke(args);
61 | client.OnError += (s, args) => OnError.Invoke(args);
62 |
63 | client.OnPresenceUpdate += (s, args) => OnPresenceUpdate.Invoke(args);
64 | client.OnSubscribe += (s, args) => OnSubscribe.Invoke(args);
65 | client.OnUnsubscribe += (s, args) => OnUnsubscribe.Invoke(args);
66 |
67 | client.OnJoin += (s, args) => OnJoin.Invoke(args);
68 | client.OnSpectate += (s, args) => OnSpectate.Invoke(args);
69 | client.OnJoinRequested += (s, args) => OnJoinRequest.Invoke(args);
70 |
71 | client.OnConnectionEstablished += (s, args) => OnConnectionEstablished.Invoke(args);
72 | client.OnConnectionFailed += (s, args) => OnConnectionFailed.Invoke(args);
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/Runtime/Control/MessageEvents.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a4ca9910408e96945acec366a9bbf00b
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/Control/UnityLogger.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using DiscordRPC.Logging;
3 |
4 | namespace Lachee.Discord.Control
5 | {
6 | ///
7 | /// This is a bridge between the Discord IPC logging and Unity Logging. Useful for debugging errors within the pipe.
8 | ///
9 | public sealed class UnityLogger : DiscordRPC.Logging.ILogger
10 | {
11 | public LogLevel Level { get; set; }
12 |
13 | public void Trace(string message, params object[] args)
14 | {
15 | if (Level > LogLevel.Trace) return;
16 | Debug.Log(" " + (args.Length > 0 ? string.Format(message, args) : message));
17 | }
18 |
19 | public void Info(string message, params object[] args)
20 | {
21 | if (Level > LogLevel.Info) return;
22 | Debug.Log(" " + (args.Length > 0 ? string.Format(message, args) : message));
23 | }
24 |
25 | public void Warning(string message, params object[] args)
26 | {
27 | if (Level > LogLevel.Warning) return;
28 | Debug.LogWarning(" " + (args.Length > 0 ? string.Format(message, args) : message));
29 | }
30 |
31 | public void Error(string message, params object[] args)
32 | {
33 | if (Level > LogLevel.Error) return;
34 | Debug.LogError(" " + (args.Length > 0 ? string.Format(message, args) : message));
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Runtime/Control/UnityLogger.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 272944aa76fcab94795bac60822c3a13
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {fileID: 2800000, guid: 776a7bd6dde298048840a64474480461, type: 3}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/Control/UnityNamedPipe.cs:
--------------------------------------------------------------------------------
1 | #if (UNITY_WSA || UNITY_WSA_10_0 || UNITY_STANDALONE) && !DISABLE_DISCORD
2 | using DiscordRPC.IO;
3 | using DiscordRPC.Logging;
4 | using System;
5 | using Lachee.IO;
6 | using System.IO;
7 | using System.Runtime.InteropServices;
8 | using System.Threading;
9 |
10 | namespace Lachee.Discord.Control
11 | {
12 | ///
13 | /// Pipe Client used to communicate with Discord.
14 | ///
15 | public class UnityNamedPipe : INamedPipeClient
16 | {
17 | const string PIPE_NAME = @"discord-ipc-{0}";
18 |
19 | private NamedPipeClientStream _stream;
20 | private byte[] _buffer = new byte[PipeFrame.MAX_SIZE];
21 |
22 | public ILogger Logger { get; set; }
23 | public bool IsConnected { get { return _stream != null && _stream.IsConnected; } }
24 | public int ConnectedPipe { get; private set; }
25 |
26 | private volatile bool _isDisposed = false;
27 |
28 | public bool Connect(int pipe)
29 | {
30 | if (_isDisposed)
31 | throw new ObjectDisposedException("NamedPipe");
32 |
33 | if (pipe > 9)
34 | throw new ArgumentOutOfRangeException("pipe", "Argument cannot be greater than 9");
35 |
36 | if (pipe < 0)
37 | {
38 | //If we have -1, then we need to iterate over every single pipe until we get it
39 | //Iterate until we connect to a pipe
40 | for (int i = 0; i < 10; i++)
41 | {
42 | if (AttemptConnection(i) || AttemptConnection(i, true))
43 | return true;
44 | }
45 |
46 | //We failed everythign else
47 | return false;
48 | }
49 | else
50 | {
51 | //We have a set one so we should just straight up try to connect to it
52 | return AttemptConnection(pipe) || AttemptConnection(pipe, true);
53 | }
54 | }
55 |
56 | private bool AttemptConnection(int pipe, bool doSandbox = false)
57 | {
58 | //Make sure the stream is null
59 | if (_stream != null)
60 | {
61 | Logger.Error("Attempted to create a new stream while one already exists!");
62 | return false;
63 | }
64 |
65 | //Make sure we are disconnected
66 | if (IsConnected)
67 | {
68 | Logger.Error("Attempted to create a new connection while one already exists!");
69 | return false;
70 | }
71 |
72 | try
73 | {
74 | //Prepare the sandbox
75 | string sandbox = doSandbox ? GetPipeSandbox() : "";
76 | if (doSandbox && sandbox == null)
77 | {
78 | Logger.Trace("Skipping sandbox because this platform does not support it.");
79 | return false;
80 | }
81 |
82 | //Prepare the name
83 | string pipename = GetPipeName(pipe);
84 |
85 | //Attempt to connect
86 | Logger.Info("Connecting to " + pipename + " (" + sandbox +")");
87 | ConnectedPipe = pipe;
88 | _stream = new NamedPipeClientStream(".", pipename);
89 | _stream.Connect();
90 |
91 | Logger.Info("Connected");
92 | return true;
93 | }
94 | catch(Exception e)
95 | {
96 | Logger.Error("Failed: " + e.GetType().FullName + ", " + e.Message);
97 | ConnectedPipe = -1;
98 | Close();
99 | return false;
100 | }
101 | }
102 |
103 | public void Close()
104 | {
105 | if (_stream != null)
106 | {
107 | Logger.Trace("Closing stream");
108 | _stream.Dispose();
109 | _stream = null;
110 | }
111 | }
112 |
113 | public void Dispose()
114 | {
115 | if (_isDisposed) return;
116 | Logger.Trace("Disposing Stream");
117 | _isDisposed = true;
118 | Close();
119 | }
120 |
121 | public bool ReadFrame(out PipeFrame frame)
122 | {
123 | if (_isDisposed)
124 | throw new ObjectDisposedException("_stream");
125 |
126 | //We are not connected so we cannot read!
127 | if (!IsConnected)
128 | {
129 | frame = default(PipeFrame);
130 | return false;
131 | }
132 |
133 | //Try and read a frame
134 | int length = _stream.Read(_buffer, 0, _buffer.Length);
135 | Logger.Trace("Read {0} bytes", length);
136 |
137 | if (length == 0)
138 | {
139 | frame = default(PipeFrame);
140 | return false;
141 | }
142 |
143 | //Read the stream now
144 | using (MemoryStream memory = new MemoryStream(_buffer, 0, length))
145 | {
146 | frame = new PipeFrame();
147 | if (!frame.ReadStream(memory))
148 | {
149 | Logger.Error("Failed to read a frame! {0}", frame.Opcode);
150 | return false;
151 | }
152 | else
153 | {
154 | Logger.Trace("Read pipe frame!");
155 | return true;
156 | }
157 | }
158 | }
159 |
160 | public bool WriteFrame(PipeFrame frame)
161 | {
162 | if (_isDisposed)
163 | throw new ObjectDisposedException("_stream");
164 |
165 | //Write the frame. We are assuming proper duplex connection here
166 | if (!IsConnected)
167 | {
168 | Logger.Error("Failed to write frame because the stream is closed");
169 | return false;
170 | }
171 |
172 | try
173 | {
174 | //Write the pipe
175 | //This can only happen on the main thread so it should be fine.
176 | Logger.Trace("Writing frame");
177 | frame.WriteStream(_stream);
178 | return true;
179 | }
180 | catch (IOException io)
181 | {
182 | Logger.Error("Failed to write frame because of a IO Exception: {0}", io.Message);
183 | }
184 | catch (ObjectDisposedException)
185 | {
186 | Logger.Warning("Failed to write frame as the stream was already disposed");
187 | }
188 | catch (InvalidOperationException)
189 | {
190 | Logger.Warning("Failed to write frame because of a invalid operation");
191 | }
192 |
193 | //We must have failed the try catch
194 | return false;
195 | }
196 |
197 | private string GetPipeName(int pipe, string sandbox = "")
198 | {
199 | switch (Environment.OSVersion.Platform)
200 | {
201 | default:
202 |
203 | #if !UNITY_EDITOR_OSX
204 | case PlatformID.Win32NT:
205 | case PlatformID.Win32S:
206 | case PlatformID.Win32Windows:
207 | case PlatformID.WinCE:
208 | Logger.Trace("PIPE WIN");
209 | return sandbox + string.Format(PIPE_NAME, pipe);
210 | #endif
211 |
212 | case PlatformID.Unix:
213 | case PlatformID.MacOSX:
214 | Logger.Trace("PIPE UNIX / MACOSX");
215 | return Path.Combine(GetEnviromentTemp(), sandbox + string.Format(PIPE_NAME, pipe));
216 | }
217 | }
218 |
219 | private string GetPipeSandbox()
220 | {
221 | switch (Environment.OSVersion.Platform)
222 | {
223 | default:
224 | return null;
225 | case PlatformID.Unix:
226 | return "snap.discord/";
227 | }
228 | }
229 |
230 | private string GetEnviromentTemp()
231 | {
232 | string temp = null;
233 | temp = temp ?? Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR");
234 | temp = temp ?? Environment.GetEnvironmentVariable("TMPDIR");
235 | temp = temp ?? Environment.GetEnvironmentVariable("TMP");
236 | temp = temp ?? Environment.GetEnvironmentVariable("TEMP");
237 | temp = temp ?? "/tmp";
238 | return temp;
239 | }
240 | }
241 | }
242 | #endif
--------------------------------------------------------------------------------
/Runtime/Control/UnityNamedPipe.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e34a1d8c83cf20b49b86cd17cf315175
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/DiscordManager.cs:
--------------------------------------------------------------------------------
1 | using DiscordRPC.Message;
2 | using Lachee.Discord.Events;
3 | using System.Collections;
4 | using UnityEngine;
5 | using UnityEngine.Events;
6 |
7 | namespace Lachee.Discord
8 | {
9 | ///
10 | /// A wrapper for the Discord Sharp Client, providing useful utilities in a Unity-Friendly form.
11 | ///
12 | [ExecuteInEditMode]
13 | public sealed class DiscordManager : MonoBehaviour
14 | {
15 | public const string EXAMPLE_APPLICATION = "424087019149328395";
16 |
17 | ///
18 | /// The current instance of the Discord Manager
19 | ///
20 | public static DiscordManager current { get { return _instance; } }
21 | private static DiscordManager _instance;
22 |
23 | #region Properties and Configurations
24 | [Header("Properties")]
25 | [Tooltip("The ID of the Discord Application. Visit the Discord API to create a new application if nessary.")]
26 | public string applicationID = EXAMPLE_APPLICATION;
27 |
28 | [Tooltip("The Steam App ID. This is a optional field used to launch your game through steam instead of the executable.")]
29 | public string steamID = "";
30 |
31 | [Tooltip("The pipe discord is located on. Useful for testing multiple clients.")]
32 | public Pipe targetPipe = Pipe.FirstAvailable;
33 |
34 | ///
35 | /// All possible pipes discord can be found on.
36 | ///
37 | public enum Pipe
38 | {
39 | FirstAvailable = -1,
40 | Pipe0 = 0,
41 | Pipe1 = 1,
42 | Pipe2 = 2,
43 | Pipe3 = 3,
44 | Pipe4 = 4,
45 | Pipe5 = 5,
46 | Pipe6 = 6,
47 | Pipe7 = 7,
48 | Pipe8 = 8,
49 | Pipe9 = 9
50 | }
51 |
52 | [Tooltip("Logging level of the Discord IPC connection.")]
53 | public DiscordRPC.Logging.LogLevel logLevel = DiscordRPC.Logging.LogLevel.Warning;
54 | [Tooltip("The file to write the logs too in a build. If empty, then the console logger will be used.")]
55 | public string logFile = "discord.log";
56 |
57 | [Tooltip("Registers a custom URI scheme for your game. This is required for the Join / Specate features to work.")]
58 | public bool registerUriScheme = false;
59 |
60 | [SerializeField]
61 | [Tooltip("The enabled state of the IPC connection")]
62 | private bool active = true;
63 |
64 | ///
65 | /// The current Discord user. This does not get set until the first Ready event.
66 | ///
67 | public User CurrentUser { get { return _currentUser; } }
68 | [Tooltip("The current Discord user. This does not get set until the first Ready event.")]
69 |
70 | [Header("State")]
71 | [SerializeField] private User _currentUser;
72 |
73 | ///
74 | /// The current event subscription flag.
75 | ///
76 | public Event CurrentSubscription { get { return _currentSubscription; } }
77 | [Tooltip("The current subscription flag")]
78 | [SerializeField] private Event _currentSubscription = Event.None;
79 |
80 | ///
81 | /// The current presence displayed on the Discord Client.
82 | ///
83 | public Presence CurrentPresence { get { return _currentPresence; } }
84 | [Tooltip("The current Rich Presence displayed on the Discord Client.")]
85 | [SerializeField] private Presence _currentPresence;
86 |
87 | #endregion
88 |
89 | [Header("Handlers and Events")]
90 | #if UNITY_2019_OR_NEWER
91 | public UnityEvent OnReady;
92 | public UnityEvent OnClose;
93 | public UnityEvent OnPresence;
94 | public UnityEvent OnJoin;
95 | [HideInInspector] public UnityEvent OnSubscribe;
96 | [HideInInspector] public UnityEvent OnUnsubscribe;
97 | [HideInInspector] public UnityEvent OnError;
98 | [HideInInspector] public UnityEvent OnSpectate;
99 | [HideInInspector] public UnityEvent OnJoinRequest;
100 | [HideInInspector] public UnityEvent OnConnectionEstablished;
101 | [HideInInspector] public UnityEvent OnConnectionFailed;
102 | #else
103 | #pragma warning disable CS0618 // Type or member is obsolete
104 | public UnityReadyEvent OnReady;
105 | public UnityCloseEvent OnClose;
106 | public UnityPresenceEvent OnPresence;
107 | public UnityJoinEvent OnJoin;
108 | [HideInInspector] public UnitySubscribeEvent OnSubscribe;
109 | [HideInInspector] public UnityUnsubscribeEvent OnUnsubscribe;
110 | [HideInInspector] public UnityErrorEvent OnError;
111 | [HideInInspector] public UnitySpectateEvent OnSpectate;
112 | [HideInInspector] public UnityJoinRequestEvent OnJoinRequest;
113 | [HideInInspector] public UnityConnectionEstablishedEvent OnConnectionEstablished;
114 | [HideInInspector] public UnityConnectionFailedEvent OnConnectionFailed;
115 | #pragma warning restore CS0618 // Type or member is obsolete
116 | #endif
117 |
118 | ///
119 | /// The current Discord Client.
120 | ///
121 | public DiscordRPC.DiscordRpcClient client { get { return _client; } }
122 | private DiscordRPC.DiscordRpcClient _client = null;
123 |
124 | public bool isInitialized { get { return _client != null && _client.IsInitialized; } }
125 |
126 | #region Unity Events
127 |
128 | private void OnDisable() { Deinitialize(); } //Try to dispose the client when we are disabled
129 | private void OnDestroy() { Deinitialize(); }
130 |
131 | #if (UNITY_WSA || UNITY_WSA_10_0 || UNITY_STANDALONE) && !DISABLE_DISCORD
132 |
133 | private void OnEnable()
134 | { //Try to initialize the client when we are enabled.
135 | if (gameObject.activeSelf && !isInitialized)
136 | Initialize();
137 | }
138 |
139 | private void Awake()
140 | {
141 | SetupSingleton();
142 | }
143 |
144 | //Try to initialize the client when we start. This is useful for moments where we are spawned in
145 | private void Start()
146 | {
147 | if (!isInitialized)
148 | {
149 | if (_client != null)
150 | {
151 | Debug.LogWarning("Client already exists! Disposing Early.");
152 | Deinitialize();
153 | }
154 |
155 | Initialize();
156 | }
157 | }
158 |
159 | private void FixedUpdate()
160 | {
161 | if (client == null) return;
162 |
163 | //Update the client log level
164 | client.Logger.Level = logLevel;
165 |
166 | //Invoke the client events
167 | client.Invoke();
168 | }
169 |
170 | #endif
171 |
172 | #if UNITY_EDITOR
173 | [UnityEditor.MenuItem("GameObject/Discord Manager", priority = 10)]
174 | private static void CreateNewManager()
175 | {
176 | var prev = FindObjectOfType();
177 | if (prev == null)
178 | {
179 | var go = new GameObject("Discord Manager");
180 | prev = go.AddComponent();
181 | }
182 | else
183 | {
184 | Debug.LogWarning("Cannot create new Discord Manager because one already exists.");
185 | }
186 |
187 | UnityEditor.Selection.activeObject = prev;
188 | }
189 | #endif
190 |
191 | #endregion
192 |
193 | private void SetupSingleton()
194 | {
195 | //This has a instance already that isn't us
196 | if (_instance != null && _instance != this)
197 | {
198 | Debug.LogWarning("[DAPI] Multiple DiscordManagers exist already. Destroying self.", _instance);
199 | Destroy(this);
200 | return;
201 | }
202 |
203 | //Make sure the client doesnt already exit
204 | if (_client != null)
205 | {
206 | Debug.LogError("[DAPI] Cannot initialize a new client when one is already initialized.");
207 | return;
208 | }
209 |
210 | //Assign the instance
211 | _instance = this;
212 |
213 | if (Application.isPlaying)
214 | DontDestroyOnLoad(this);
215 | }
216 |
217 | ///
218 | /// Initializes the discord client if able to. Wont initialize if is false, we are not in playmode, we already have a instance or we already have a client.
219 | /// This function is empty unless UNITY_WSA || UNITY_WSA_10_0 || UNITY_STANDALONE) && !DISABLE_DISCORD is meet.
220 | ///
221 | public void Initialize()
222 | {
223 | #if (UNITY_WSA || UNITY_WSA_10_0 || UNITY_STANDALONE) && !DISABLE_DISCORD
224 |
225 | if (!active) return; //Are we allowed to be active?
226 | if (!Application.isPlaying) return; //We are not allowed to initialize while in the editor.
227 |
228 | SetupSingleton();
229 |
230 | //Prepare the logger
231 | DiscordRPC.Logging.ILogger logger = null;
232 |
233 | //Update the logger to the unity logger
234 | if (Application.isEditor)
235 | {
236 | logger = new Control.UnityLogger() { Level = logLevel };
237 | }
238 | else
239 | {
240 | logger = new DiscordRPC.Logging.FileLogger(logFile) { Level = logLevel };
241 | }
242 |
243 | //We are starting the client. Below is a break down of the parameters.
244 | Debug.Log("[DRP] Starting Discord Rich Presence");
245 | _client = new DiscordRPC.DiscordRpcClient(
246 | applicationID, //The Discord Application ID
247 | pipe: (int)targetPipe, //The target pipe to connect too
248 | logger: logger, //The logger,
249 | autoEvents: false, //WE will manually invoke events
250 | client: new Control.UnityNamedPipe() //The client for the pipe to use. Unity MUST use a NativeNamedPipeClient since its managed client is broken.
251 | );
252 |
253 | if (registerUriScheme)
254 | client.RegisterUriScheme(steamID);
255 |
256 | //Subscribe to some initial events
257 | #region Event Registration
258 | client.OnError += (s, args) => Debug.LogError("[DRP] Error Occured within the Discord IPC: (" + args.Code + ") " + args.Message);
259 | client.OnJoinRequested += (s, args) => Debug.Log("[DRP] Join Requested");
260 |
261 | client.OnReady += (s, args) =>
262 | {
263 | //We have connected to the Discord IPC. We should send our rich presence just incase it lost it.
264 | Debug.Log("[DRP] Connection established and received READY from Discord IPC. Sending our previous Rich Presence and Subscription.");
265 |
266 | //Set the user and cache their avatars
267 | _currentUser = args.User;
268 | _currentUser.GetAvatar(DiscordAvatarSize.x128);
269 | };
270 | client.OnPresenceUpdate += (s, args) =>
271 | {
272 | Debug.Log("[DRP] Our Rich Presence has been updated. Applied changes to local store.");
273 | Debug.Log(args.Presence.State);
274 | _currentPresence = (Presence)args.Presence;
275 | };
276 | client.OnSubscribe += (s, a) =>
277 | {
278 | Debug.Log("[DRP] New Subscription. Updating local store.");
279 | _currentSubscription = client.Subscription.ToUnity();
280 | };
281 | client.OnUnsubscribe += (s, a) =>
282 | {
283 | Debug.Log("[DRP] Removed Subscription. Updating local store.");
284 | _currentSubscription = client.Subscription.ToUnity();
285 | };
286 |
287 | //Register the unity events
288 | client.OnReady += (s, args) => OnReady?.Invoke(new ReadyEvent(args));
289 | client.OnClose += (s, args) => OnClose?.Invoke(args);
290 | client.OnError += (s, args) => OnError?.Invoke(args);
291 |
292 | client.OnPresenceUpdate += (s, args) => OnPresence?.Invoke(new PresenceEvent(args));
293 | client.OnSubscribe += (s, args) => OnSubscribe?.Invoke(args);
294 | client.OnUnsubscribe += (s, args) => OnUnsubscribe?.Invoke(args);
295 |
296 | client.OnJoin += (s, args) => OnJoin?.Invoke(args);
297 | client.OnSpectate += (s, args) => OnSpectate?.Invoke(args);
298 | client.OnJoinRequested += (s, args) => OnJoinRequest?.Invoke(new JoinRequestEvent(args));
299 |
300 | client.OnConnectionEstablished += (s, args) => OnConnectionEstablished.Invoke(args);
301 | client.OnConnectionFailed += (s, args) => OnConnectionFailed.Invoke(args);
302 | #endregion
303 |
304 | //Set initial presence and sub. (This will enqueue it)
305 | SetSubscription(_currentSubscription);
306 | SetPresence(_currentPresence);
307 |
308 | //Start the client
309 | _client.Initialize();
310 | Debug.Log("[DRP] Discord Rich Presence intialized and connecting...");
311 |
312 | #endif
313 | }
314 |
315 | ///
316 | /// If not already disposed, it will dispose and deinitialize the discord client.
317 | ///
318 | public void Deinitialize()
319 | {
320 | //We dispose outside the scripting symbols as we always want to be able to dispose (just in case).
321 | if (_client != null)
322 | {
323 | Debug.Log("[DRP] Disposing Discord IPC Client...");
324 | _client.Dispose();
325 | _client = null;
326 | Debug.Log("[DRP] Finished Disconnecting");
327 | }
328 | }
329 |
330 | ///
331 | /// Sets the Rich Presence of the Discord Client through the pipe connection.
332 | /// This will log a error if the client is null or not yet initiated.
333 | ///
334 | /// The Rich Presence to be shown to the client
335 | public void SetPresence(Presence presence)
336 | {
337 | if (client == null)
338 | {
339 | Debug.LogError("[DRP] Attempted to send a presence update but no client exists!");
340 | return;
341 | }
342 |
343 | if (!client.IsInitialized)
344 | {
345 | //Debug.LogWarning("[DRP] Attempted to send a presence update to a client that is not initialized! The messages will be enqueued instead!");
346 | }
347 |
348 | //Just do some validation
349 | if (!presence.secrets.IsEmpty() && _currentSubscription == Event.None)
350 | {
351 | Debug.LogWarning("[DRP] Sending a secret, however we are not actually subscribed to any events. This will cause the messages to be ignored!");
352 | }
353 |
354 | //Set the presence
355 | _currentPresence = presence;
356 | client.SetPresence(presence != null ? presence.ToRichPresence() : null);
357 | }
358 |
359 | ///
360 | /// Resends the current Rich Presence to the Discord Client via the pipe connectoin.
361 | ///
362 | [ContextMenu("Resend Presence")]
363 | public void ResetPresence()
364 | {
365 | SetPresence(_currentPresence);
366 | }
367 |
368 | ///
369 | /// Sets the subscription flag, unsubscribing and then subscribing to the nessary events. Used for Join / Spectate feature. If you have not registered your application, this feature is unavailable.
370 | /// This will log a error if the client is null or not yet initiated.
371 | ///
372 | /// The events to subscribe too
373 | public void SetSubscription(Event evt)
374 | {
375 | if (client == null)
376 | {
377 | Debug.LogError("[DRP] Attempted to send a presence update but no client exists!");
378 | return;
379 | }
380 |
381 | //if (!client.IsInitialized)
382 | //{
383 | // Debug.LogError("[DRP] Attempted to send a presence update to a client that is not initialized!");
384 | // return;
385 | //}
386 |
387 | this._currentSubscription = evt;
388 | client.SetSubscription(evt.ToDiscordRPC());
389 | }
390 |
391 | #region Single Components Sets
392 | public Presence UpdateDetails(string details)
393 | {
394 | if (_client == null) return null;
395 | return (Presence)_client.UpdateDetails(details);
396 | }
397 |
398 | public Presence UpdateState(string state)
399 | {
400 | if (_client == null) return null;
401 | return (Presence)_client.UpdateState(state);
402 | }
403 |
404 | public Presence UpdateParty(Party party)
405 | {
406 | if (_client == null) return null;
407 | if (party == null) return (Presence)_client.UpdateParty(null);
408 | return (Presence)_client.UpdateParty(party.ToRichParty());
409 | }
410 | public Presence UpdatePartySize(int size, int max)
411 | {
412 | if (_client == null) return null;
413 | return (Presence)_client.UpdatePartySize(size, max);
414 | }
415 |
416 | public Presence UpdateLargeAsset(Asset asset)
417 | {
418 | if (_client == null) return null;
419 | if (asset == null) return (Presence)_client.UpdateLargeAsset("", "");
420 | return (Presence)_client.UpdateLargeAsset(asset.image, asset.tooltip);
421 | }
422 | public Presence UpdateSmallAsset(Asset asset)
423 | {
424 | if (_client == null) return null;
425 | if (asset == null) return (Presence)_client.UpdateSmallAsset("", "");
426 | return (Presence)_client.UpdateSmallAsset(asset.image, asset.tooltip);
427 | }
428 |
429 | public Presence UpdateSecrets(Secrets secrets)
430 | {
431 | if (_client == null) return null;
432 | return (Presence)_client.UpdateSecrets(secrets.ToRichSecrets());
433 | }
434 |
435 | public Presence UpdateStartTime()
436 | {
437 | if (_client == null) return null;
438 | return (Presence)_client.UpdateStartTime();
439 | }
440 | public Presence UpdateStartTime(Timestamp timestamp)
441 | {
442 | if (_client == null) return null;
443 | return (Presence)_client.UpdateStartTime(timestamp.GetDateTime());
444 | }
445 | public Presence UpdateEndTime()
446 | {
447 | if (_client == null) return null;
448 | return (Presence)_client.UpdateEndTime();
449 | }
450 | public Presence UpdateEndTime(Timestamp timestamp)
451 | {
452 | if (_client == null) return null;
453 | return (Presence)_client.UpdateEndTime(timestamp.GetDateTime());
454 | }
455 | public Presence UpdateClearTime(Timestamp timestamp)
456 | {
457 | if (_client == null) return null;
458 | return (Presence)_client.UpdateClearTime();
459 | }
460 | #endregion
461 |
462 |
463 | ///
464 | /// Resonds to a Join Request.
465 | ///
466 | /// The request being responded too
467 | /// The result of the request. True to accept the request.
468 | public void Respond(JoinRequestMessage request, bool acceptRequest)
469 | {
470 | if (client == null)
471 | {
472 | Debug.LogError("[DRP] Attempted to send a presence update but no client exists!");
473 | return;
474 | }
475 |
476 | if (!client.IsInitialized)
477 | {
478 | Debug.LogError("[DRP] Attempted to send a presence update to a client that is not initialized!");
479 | return;
480 | }
481 |
482 |
483 | client.Respond(request, acceptRequest);
484 | }
485 | }
486 | }
487 |
--------------------------------------------------------------------------------
/Runtime/DiscordManager.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b1c0662f7bb58e7439856b9fa8414d6c
3 | timeCreated: 1521360783
4 | licenseType: Free
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {fileID: 2800000, guid: 0e82c8e5c734e424d827caf275b12cb9, type: 3}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/Runtime/Events.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 92f7f8d3f3cb974489194c82ff3d7ac4
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/Events/Events.cs:
--------------------------------------------------------------------------------
1 | using DiscordRPC;
2 | using DiscordRPC.Message;
3 | using System;
4 | using UnityEngine;
5 | using UnityEngine.Events;
6 |
7 | namespace Lachee.Discord.Events
8 | {
9 | public sealed class ReadyEvent
10 | {
11 | public User user { get; }
12 | public Configuration configuration { get; }
13 | public int version { get; }
14 |
15 | internal ReadyEvent(ReadyMessage message)
16 | {
17 | user = new User(message.User);
18 | configuration = message.Configuration;
19 | version = message.Version;
20 | }
21 | }
22 |
23 | public sealed class PresenceEvent
24 | {
25 | public Presence presence { get; }
26 | public string name { get; }
27 | public string applicationID { get; }
28 | internal PresenceEvent(PresenceMessage message)
29 | {
30 | presence = new Presence(message.Presence);
31 | name = message.Name;
32 | applicationID = message.ApplicationID;
33 | }
34 | }
35 |
36 | public sealed class JoinRequestEvent
37 | {
38 | public User user { get; }
39 | internal JoinRequestEvent(JoinRequestMessage message)
40 | {
41 | user = new User(message.User);
42 | }
43 | }
44 |
45 |
46 | // By Unity 2020, you shouldn't be using these anymore
47 | #if !UNITY_2020_OR_NEWER
48 | [Serializable]
49 | [System.Obsolete("This is a wrapper for Unity 2018")]
50 | public class UnityReadyEvent : UnityEvent { }
51 |
52 | [Serializable]
53 | [System.Obsolete("This is a wrapper for Unity 2018")]
54 | public class UnityCloseEvent : UnityEvent { }
55 |
56 | [Serializable]
57 | [System.Obsolete("This is a wrapper for Unity 2018")]
58 | public class UnityErrorEvent : UnityEvent { }
59 |
60 | [Serializable]
61 | [System.Obsolete("This is a wrapper for Unity 2018")]
62 | public class UnityPresenceEvent : UnityEvent { }
63 |
64 | [Serializable]
65 | [System.Obsolete("This is a wrapper for Unity 2018")]
66 | public class UnitySubscribeEvent : UnityEvent { }
67 |
68 | [Serializable]
69 | [System.Obsolete("This is a wrapper for Unity 2018")]
70 | public class UnityUnsubscribeEvent : UnityEvent { }
71 |
72 | [Serializable]
73 | [System.Obsolete("This is a wrapper for Unity 2018")]
74 | public class UnityJoinEvent : UnityEvent { }
75 |
76 | [Serializable]
77 | [System.Obsolete("This is a wrapper for Unity 2018")]
78 | public class UnitySpectateEvent : UnityEvent { }
79 |
80 | [Serializable]
81 | [System.Obsolete("This is a wrapper for Unity 2018")]
82 | public class UnityJoinRequestEvent : UnityEvent { }
83 |
84 | [Serializable]
85 | [System.Obsolete("This is a wrapper for Unity 2018")]
86 | public class UnityConnectionEstablishedEvent : UnityEvent { }
87 |
88 | [Serializable][System.Obsolete("This is a wrapper for Unity 2018")]
89 | public class UnityConnectionFailedEvent : UnityEvent { }
90 | #endif
91 | }
--------------------------------------------------------------------------------
/Runtime/Events/Events.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: eac5fab1ecfe2484797c1b1f260aae76
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/Link.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/Runtime/Link.xml.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5a228e4a5dcb1b448877e9e5692c4425
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b1aef380728eae1459ca601833d41f4e
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/IO.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5b69ff6be5f109f41bd912d42a8db1d3
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/IO/Exceptions.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ba58dc8e6978d674ebef739c0260b54b
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/IO/Exceptions/NamedPipeConnectionException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Lachee.IO.Exceptions
4 | {
5 | public class NamedPipeConnectionException : Exception
6 | {
7 | internal NamedPipeConnectionException(string message) : base(message) { }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/IO/Exceptions/NamedPipeConnectionException.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f1d001c44226e5d4f98cddad90dbf94e
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/IO/Exceptions/NamedPipeOpenException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Lachee.IO.Exceptions
4 | {
5 | public class NamedPipeOpenException : Exception
6 | {
7 | public int ErrorCode { get; private set; }
8 | internal NamedPipeOpenException(int err) : base("An exception has occured while trying to open the pipe. Error Code: " + err)
9 | {
10 | ErrorCode = err;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/IO/Exceptions/NamedPipeOpenException.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: cb6c26b3e8a8d614d8184df301dee1e6
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/IO/Exceptions/NamedPipeReadException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Lachee.IO.Exceptions
4 | {
5 | public class NamedPipeReadException : Exception
6 | {
7 | public int ErrorCode { get; private set; }
8 | internal NamedPipeReadException(int err) : base("An exception occured while reading from the pipe. Error Code: " + err)
9 | {
10 | ErrorCode = err;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/IO/Exceptions/NamedPipeReadException.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 51431e2170eb34441b9d558a6d90a29c
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/IO/Exceptions/NamedPipeWriteException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Lachee.IO.Exceptions
4 | {
5 | public class NamedPipeWriteException : Exception
6 | {
7 | public int ErrorCode { get; private set; }
8 | internal NamedPipeWriteException(int err) : base("An exception occured while reading from the pipe. Error Code: " + err)
9 | {
10 | ErrorCode = err;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/IO/Exceptions/NamedPipeWriteException.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 32a12cea90d19f041ac631369ba6816f
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/IO/NamedPipeClientStream.cs:
--------------------------------------------------------------------------------
1 | using Lachee.IO.Exceptions;
2 | using System;
3 | using System.IO;
4 | using System.Runtime.InteropServices;
5 |
6 | namespace Lachee.IO
7 | {
8 | public class NamedPipeClientStream : System.IO.Stream
9 | {
10 | private IntPtr ptr;
11 | private bool _isDisposed;
12 |
13 | ///
14 | /// Can the stream read? Always returns true.
15 | ///
16 | public override bool CanRead { get { return true; } }
17 |
18 | ///
19 | /// Can the stream seek? Always returns false.
20 | ///
21 | public override bool CanSeek { get { return false; } }
22 |
23 | ///
24 | /// Can the stream write? Always returns true.
25 | ///
26 | public override bool CanWrite { get { return true; } }
27 |
28 | ///
29 | /// The length of the stream. Always 0.
30 | ///
31 | public override long Length { get { return 0; } }
32 |
33 | ///
34 | /// The current position of the stream. Always 0.
35 | ///
36 | public override long Position { get { return 0; } set { } }
37 |
38 | ///
39 | /// Checks if the current pipe is connected and running.
40 | ///
41 | public bool IsConnected { get { return Native.IsConnected(ptr); } }
42 |
43 | ///
44 | /// The pipe name for this client.
45 | ///
46 | public string PipeName { get; private set; }
47 |
48 | #region Constructors
49 | ///
50 | /// Creates a new instance of a NamedPipeClient
51 | ///
52 | /// The remote to connect too
53 | /// The name of the pipe that will be connected too.
54 | public NamedPipeClientStream(string server, string pipeName)
55 | {
56 | ptr = Native.CreateClient();
57 | PipeName = FormatPipe(server, pipeName);
58 | Console.WriteLine("Created new NamedPipeClientStream '{0}' => '{1}'", pipeName, PipeName);
59 | }
60 |
61 | ~NamedPipeClientStream()
62 | {
63 | Dispose(false);
64 | }
65 |
66 | protected override void Dispose(bool disposing)
67 | {
68 | base.Dispose(disposing);
69 | if (!_isDisposed)
70 | {
71 | Disconnect();
72 | Native.DestroyClient(ptr);
73 | _isDisposed = true;
74 | }
75 | }
76 |
77 | private static string FormatPipe(string server, string pipeName)
78 | {
79 | switch (Environment.OSVersion.Platform)
80 | {
81 | default:
82 | case PlatformID.Win32NT:
83 | return string.Format(@"\\{0}\pipe\{1}", server, pipeName);
84 |
85 | case PlatformID.Unix:
86 | if (server != ".")
87 | throw new PlatformNotSupportedException("Remote pipes are not supported on this platform.");
88 | return pipeName;
89 | }
90 | }
91 |
92 | #endregion
93 |
94 | #region Open Close
95 | ///
96 | /// Attempts to open a named pipe.
97 | ///
98 | /// The name of the pipe
99 | public void Connect()
100 | {
101 | int code = Native.Open(ptr, PipeName);
102 | if (!IsConnected) throw new NamedPipeOpenException(code);
103 |
104 | }
105 |
106 | ///
107 | /// Closes the named pipe already opened.
108 | ///
109 | public void Disconnect()
110 | {
111 | Native.Close(ptr);
112 | }
113 | #endregion
114 |
115 | ///
116 | /// Reads a block of bytes from a stream and writes the data to a specified buffer. Will not block if there is no data available to read.
117 | ///
118 | /// When this method returns, contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source.
119 | /// The byte offset in the buffer array at which the bytes that are read will be placed.
120 | /// The maximum number of bytes to read.
121 | /// The total number of bytes that are read into buffer. This might be less than the number of bytes requested if that number of bytes is not currently available. If the value is less than 0, then a error has occured.
122 | public override int Read(byte[] buffer, int offset, int count)
123 | {
124 | //Make sure we are connected
125 | if (!IsConnected)
126 | throw new NamedPipeConnectionException("Cannot read stream as pipe is not connected");
127 |
128 | if (offset + count > buffer.Length)
129 | throw new ArgumentOutOfRangeException("count", "Cannot read as the count exceeds the buffer size");
130 |
131 | //Prepare bytes read and a buffer of memory to read into
132 | int bytesRead = 0;
133 | int size = Marshal.SizeOf(buffer[0]) * count;
134 | IntPtr buffptr = Marshal.AllocHGlobal(size);
135 |
136 | try
137 | {
138 | //Read the bytes
139 | bytesRead = Native.ReadFrame(ptr, buffptr, count);
140 | if (bytesRead <= 0)
141 | {
142 | //A error actively occured. If it is 0 we just read no bytes.
143 | if (bytesRead < 0)
144 | {
145 | //We have a pretty bad error, we will log it for prosperity.
146 | throw new NamedPipeReadException(bytesRead);
147 | }
148 |
149 | //Return a empty frame and return false (read failure).
150 | return 0;
151 | }
152 | else
153 | {
154 | //WE have read a valid amount of bytes, so copy the marshaled bytes over to the buffer
155 | Marshal.Copy(buffptr, buffer, offset, bytesRead);
156 | return bytesRead;
157 | }
158 | }
159 | finally
160 | {
161 | //Finally, before we exit this try block, free the pointer we allocated.
162 | Marshal.FreeHGlobal(buffptr);
163 | }
164 | }
165 |
166 | ///
167 | /// Writes a block of bytes to the current stream using data from a buffer
168 | ///
169 | /// The buffer that contains data to write to the pipe.
170 | /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream.
171 | /// The maximum number of bytes to write to the current stream.
172 | public override void Write(byte[] buffer, int offset, int count)
173 | {
174 | //Make sure we are connected
175 | if (!IsConnected)
176 | throw new NamedPipeConnectionException("Cannot write stream as pipe is not connected");
177 |
178 | //Copy the bytes into a new marshaled block
179 | int size = Marshal.SizeOf(buffer[0]) * count;
180 | IntPtr buffptr = Marshal.AllocHGlobal(size);
181 |
182 | try
183 | {
184 | //Copy the block of memory over
185 | Marshal.Copy(buffer, offset, buffptr, count);
186 |
187 | //Send the block
188 | int result = Native.WriteFrame(ptr, buffptr, count);
189 | if (result < 0) throw new NamedPipeWriteException(result);
190 | }
191 | finally
192 | {
193 | //Finally, before exiting the try catch, free the memory we assigned.
194 | Marshal.FreeHGlobal(buffptr);
195 | }
196 | }
197 |
198 | #region unsupported
199 |
200 | ///
201 | /// Flushes the stream. Not supported by NamedPipeClient.
202 | ///
203 | public override void Flush()
204 | {
205 | throw new NotSupportedException();
206 | }
207 |
208 | ///
209 | /// Seeks to the given posisiton. Not supported by NamedPipeClient
210 | ///
211 | ///
212 | ///
213 | ///
214 | public override long Seek(long offset, SeekOrigin origin)
215 | {
216 | throw new NotSupportedException();
217 | }
218 |
219 | ///
220 | /// Sets the length of the stream. Not supported by NamedPipeClient
221 | ///
222 | ///
223 | public override void SetLength(long value)
224 | {
225 | throw new NotSupportedException();
226 | }
227 |
228 | #endregion
229 |
230 | private static class Native
231 | {
232 | const string LIBRARY_NAME = "NativeNamedPipe";
233 |
234 | #region Creation and Destruction
235 | [DllImport(LIBRARY_NAME, EntryPoint = "createClient", CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)]
236 | public static extern IntPtr CreateClient();
237 |
238 | [DllImport(LIBRARY_NAME, EntryPoint = "destroyClient", CallingConvention = CallingConvention.Cdecl)]
239 | public static extern void DestroyClient(IntPtr client);
240 | #endregion
241 |
242 | #region State Control
243 |
244 | [DllImport(LIBRARY_NAME, EntryPoint = "isConnected", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
245 | public static extern bool IsConnected([MarshalAs(UnmanagedType.SysInt)] IntPtr client);
246 |
247 | [DllImport(LIBRARY_NAME, EntryPoint = "open", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
248 | public static extern int Open(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string pipename);
249 |
250 | [DllImport(LIBRARY_NAME, EntryPoint = "close", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
251 | public static extern void Close(IntPtr client);
252 |
253 | #endregion
254 |
255 | #region IO
256 |
257 | [DllImport(LIBRARY_NAME, EntryPoint = "readFrame", CallingConvention = CallingConvention.Cdecl)]
258 | public static extern int ReadFrame(IntPtr client, IntPtr buffer, int length);
259 |
260 | [DllImport(LIBRARY_NAME, EntryPoint = "writeFrame", CallingConvention = CallingConvention.Cdecl)]
261 | public static extern int WriteFrame(IntPtr client, IntPtr buffer, int length);
262 |
263 | #endregion
264 | }
265 | }
266 | }
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/IO/NamedPipeClientStream.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 41be55c1bc70ab3488e70590dca4653b
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/Plugins.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5e7cf9ef4dddb0143adf5c8b62721b7a
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/Plugins/x86_64.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a2aea989833f9064cbbccef4bf9bc108
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/Plugins/x86_64/NativeNamedPipe.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lachee/discord-rpc-unity/7498674d0192bb8bb68f417c13b57884b50144e3/Runtime/NamedPipeClient/Plugins/x86_64/NativeNamedPipe.dll
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/Plugins/x86_64/NativeNamedPipe.dll.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c997a7f967dc5854e894fcb413191317
3 | PluginImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | iconMap: {}
7 | executionOrder: {}
8 | isPreloaded: 0
9 | isOverridable: 0
10 | platformData:
11 | - first:
12 | '': Any
13 | second:
14 | enabled: 0
15 | settings:
16 | Exclude Editor: 0
17 | Exclude Linux: 0
18 | Exclude Linux64: 0
19 | Exclude LinuxUniversal: 0
20 | Exclude OSXUniversal: 0
21 | Exclude Win: 1
22 | Exclude Win64: 0
23 | - first:
24 | '': OSXIntel
25 | second:
26 | enabled: 0
27 | settings:
28 | CPU: None
29 | - first:
30 | '': OSXIntel64
31 | second:
32 | enabled: 1
33 | settings:
34 | CPU: AnyCPU
35 | - first:
36 | Any:
37 | second:
38 | enabled: 1
39 | settings: {}
40 | - first:
41 | Editor: Editor
42 | second:
43 | enabled: 1
44 | settings:
45 | CPU: x86_64
46 | DefaultValueInitialized: true
47 | OS: Windows
48 | - first:
49 | Facebook: Win
50 | second:
51 | enabled: 0
52 | settings:
53 | CPU: None
54 | - first:
55 | Facebook: Win64
56 | second:
57 | enabled: 1
58 | settings:
59 | CPU: AnyCPU
60 | - first:
61 | Standalone: Linux
62 | second:
63 | enabled: 1
64 | settings:
65 | CPU: None
66 | - first:
67 | Standalone: Linux64
68 | second:
69 | enabled: 1
70 | settings:
71 | CPU: x86_64
72 | - first:
73 | Standalone: LinuxUniversal
74 | second:
75 | enabled: 1
76 | settings:
77 | CPU: AnyCPU
78 | - first:
79 | Standalone: OSXUniversal
80 | second:
81 | enabled: 1
82 | settings:
83 | CPU: x86_64
84 | - first:
85 | Standalone: Win
86 | second:
87 | enabled: 0
88 | settings:
89 | CPU: None
90 | - first:
91 | Standalone: Win64
92 | second:
93 | enabled: 1
94 | settings:
95 | CPU: AnyCPU
96 | userData:
97 | assetBundleName:
98 | assetBundleVariant:
99 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/Plugins/x86_64/NativeNamedPipe.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lachee/discord-rpc-unity/7498674d0192bb8bb68f417c13b57884b50144e3/Runtime/NamedPipeClient/Plugins/x86_64/NativeNamedPipe.so
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/Plugins/x86_64/NativeNamedPipe.so.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: afdd54d5193832a46a03889bcfbd790c
3 | PluginImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | iconMap: {}
7 | executionOrder: {}
8 | isPreloaded: 0
9 | isOverridable: 0
10 | platformData:
11 | - first:
12 | '': Any
13 | second:
14 | enabled: 0
15 | settings:
16 | Exclude Editor: 0
17 | Exclude Linux: 1
18 | Exclude Linux64: 0
19 | Exclude LinuxUniversal: 0
20 | Exclude OSXUniversal: 0
21 | Exclude Win: 0
22 | Exclude Win64: 0
23 | - first:
24 | '': OSXIntel
25 | second:
26 | enabled: 0
27 | settings:
28 | CPU: None
29 | - first:
30 | '': OSXIntel64
31 | second:
32 | enabled: 1
33 | settings:
34 | CPU: AnyCPU
35 | - first:
36 | Any:
37 | second:
38 | enabled: 1
39 | settings: {}
40 | - first:
41 | Editor: Editor
42 | second:
43 | enabled: 1
44 | settings:
45 | CPU: x86_64
46 | DefaultValueInitialized: true
47 | OS: AnyOS
48 | - first:
49 | Facebook: Win
50 | second:
51 | enabled: 0
52 | settings:
53 | CPU: None
54 | - first:
55 | Facebook: Win64
56 | second:
57 | enabled: 1
58 | settings:
59 | CPU: AnyCPU
60 | - first:
61 | Standalone: Linux
62 | second:
63 | enabled: 0
64 | settings:
65 | CPU: None
66 | - first:
67 | Standalone: Linux64
68 | second:
69 | enabled: 1
70 | settings:
71 | CPU: x86_64
72 | - first:
73 | Standalone: LinuxUniversal
74 | second:
75 | enabled: 1
76 | settings:
77 | CPU: x86_64
78 | - first:
79 | Standalone: OSXUniversal
80 | second:
81 | enabled: 1
82 | settings:
83 | CPU: x86_64
84 | - first:
85 | Standalone: Win
86 | second:
87 | enabled: 1
88 | settings:
89 | CPU: None
90 | - first:
91 | Standalone: Win64
92 | second:
93 | enabled: 1
94 | settings:
95 | CPU: AnyCPU
96 | userData:
97 | assetBundleName:
98 | assetBundleVariant:
99 |
--------------------------------------------------------------------------------
/Runtime/NamedPipeClient/ReadMe.rtf.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 95ea1e8a74bdfd1429d606aa37c3cad8
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Runtime/Plugins.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 20f1344c265ee8e41b65471ea362cfd3
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/Plugins/DiscordRPC.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lachee/discord-rpc-unity/7498674d0192bb8bb68f417c13b57884b50144e3/Runtime/Plugins/DiscordRPC.dll
--------------------------------------------------------------------------------
/Runtime/Plugins/DiscordRPC.dll.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 95439ed501e6ee64b88375393b71fe09
3 | PluginImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | iconMap: {}
7 | executionOrder: {}
8 | defineConstraints: []
9 | isPreloaded: 0
10 | isOverridable: 1
11 | isExplicitlyReferenced: 0
12 | validateReferences: 1
13 | platformData:
14 | - first:
15 | Any:
16 | second:
17 | enabled: 1
18 | settings: {}
19 | - first:
20 | Editor: Editor
21 | second:
22 | enabled: 0
23 | settings:
24 | DefaultValueInitialized: true
25 | - first:
26 | Windows Store Apps: WindowsStoreApps
27 | second:
28 | enabled: 0
29 | settings:
30 | CPU: AnyCPU
31 | userData:
32 | assetBundleName:
33 | assetBundleVariant:
34 |
--------------------------------------------------------------------------------
/Runtime/Plugins/DiscordRPC.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lachee/discord-rpc-unity/7498674d0192bb8bb68f417c13b57884b50144e3/Runtime/Plugins/DiscordRPC.pdb
--------------------------------------------------------------------------------
/Runtime/Plugins/DiscordRPC.pdb.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d1c3fadb7f94457448ba636689abb6ea
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Runtime/Plugins/DiscordRPC.xml.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 40060c82d7c647f439872eb6ed484fbb
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Runtime/Presence.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 82a89a20c498d824d970515bcf229cec
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/Presence/Asset.cs:
--------------------------------------------------------------------------------
1 | using Lachee.Discord.Attributes;
2 | using UnityEngine;
3 |
4 | namespace Lachee.Discord
5 | {
6 | [System.Obsolete("The word Discord has been removed from types", true)]
7 | public sealed class DiscordAsset { }
8 |
9 | [System.Serializable]
10 | public sealed class Asset
11 | {
12 | ///
13 | /// The key of the image to be displayed.
14 | /// Max 32 Bytes.
15 | ///
16 | [CharacterLimit(256, enforce = true)]
17 | [Tooltip("The key or URL of the image to be displayed in the large square.")]
18 | public string image;
19 |
20 | ///
21 | /// The tooltip of the image.
22 | /// Max 128 Bytes.
23 | ///
24 | [CharacterLimit(128, enforce = true)]
25 | [Tooltip("The tooltip of the image.")]
26 | public string tooltip;
27 |
28 | [Tooltip("Snowflake ID of the image.")]
29 | public ulong snowflake;
30 |
31 | ///
32 | /// Is the asset object empty?
33 | ///
34 | ///
35 | public bool IsEmpty()
36 | {
37 | return string.IsNullOrEmpty(image) && string.IsNullOrEmpty(tooltip);
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/Runtime/Presence/Asset.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0323152162a0a9c43a683c36ac6086cf
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/Presence/Button.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace Lachee.Discord
4 | {
5 | [System.Obsolete("The word Discord has been removed from types", true)]
6 | public sealed class DiscordButton { }
7 |
8 | [System.Serializable]
9 | public sealed class Button
10 | {
11 | ///
12 | /// The text on the button to be displayed
13 | ///
14 | [Tooltip("The label on the button to be displayed")]
15 | public string label;
16 |
17 | ///
18 | /// The tooltip of the image.
19 | /// ]
20 | [Tooltip("The URL on the button to be displayed")]
21 | public string url;
22 |
23 | ///
24 | /// Is the asset object empty?
25 | ///
26 | ///
27 | public bool IsEmpty()
28 | {
29 | return string.IsNullOrEmpty(label) && string.IsNullOrEmpty(url);
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/Runtime/Presence/Button.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 180aa9f983e1fde4986427b689918038
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/Presence/Event.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace Lachee.Discord
4 | {
5 | ///
6 | /// Events to receive from Discord
7 | ///
8 | [System.Flags]
9 | public enum Event
10 | {
11 | ///
12 | /// No Events
13 | ///
14 | None = 0,
15 |
16 | ///
17 | /// Listen to Spectate Events
18 | ///
19 | Spectate = 1,
20 |
21 | ///
22 | /// Listen to Join Events
23 | ///
24 | Join = 2,
25 |
26 | ///
27 | /// Listen for Join Requests
28 | ///
29 | JoinRequest = 4
30 | }
31 |
32 | public static class EventExtension
33 | {
34 | public static DiscordRPC.EventType ToDiscordRPC(this Event ev)
35 | {
36 | return (DiscordRPC.EventType)((int)ev);
37 | }
38 |
39 | public static Event ToUnity(this DiscordRPC.EventType type)
40 | {
41 | return (Event)((int)type);
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/Runtime/Presence/Event.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 089832a7aac7b8f43bb9192a403658c0
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/Presence/Party.cs:
--------------------------------------------------------------------------------
1 | using Lachee.Discord.Attributes;
2 | using UnityEngine;
3 |
4 | namespace Lachee.Discord
5 | {
6 | [System.Obsolete("The word Discord has been removed from types", true)]
7 | public sealed class DiscordParty { }
8 |
9 | [System.Serializable]
10 | public sealed class Party
11 | {
12 | ///
13 | /// A unique ID for the player's current party / lobby / group. If this is not supplied, they player will not be in a party and the rest of the information will not be sent.
14 | /// Max 128 Bytes
15 | ///
16 | [CharacterLimit(128)]
17 | [Tooltip("The unique ID of the party. Leave empty for no party.")]
18 | public string identifer;
19 |
20 | ///
21 | /// The size of the party.
22 | ///
23 | [Tooltip("The current size of the party.")]
24 | public int size;
25 |
26 | ///
27 | /// The max size of the party. Cannot be smaller than size.
28 | ///
29 | [Tooltip("The max size of the party. Cannot be smaller than size.")]
30 | public int maxSize;
31 |
32 | ///
33 | /// Creates a new instance of the party
34 | ///
35 | /// ID of the party
36 | /// Size of the party
37 | /// Max Size of the party
38 | public Party(string id, int size, int max)
39 | {
40 | this.identifer = id;
41 | this.size = size;
42 | this.maxSize = max;
43 | }
44 |
45 | ///
46 | /// Creates a new empty instance of the party
47 | ///
48 | public Party() { }
49 |
50 | ///
51 | /// Returns true if the party is not valid and has no ID.
52 | ///
53 | ///
54 | public bool IsEmpty()
55 | {
56 | return size <= 0 || maxSize <= 0 || string.IsNullOrEmpty(identifer);
57 | }
58 |
59 | ///
60 | /// Creates new instances of the party, using the as the base.
61 | ///
62 | /// The base to use the values from
63 | public Party(DiscordRPC.Party party)
64 | {
65 | this.identifer = party.ID;
66 | this.size = party.Size;
67 | this.maxSize = party.Max;
68 | }
69 |
70 | ///
71 | /// Converts this object into the DiscordRPC equivilent.
72 | ///
73 | ///
74 | public DiscordRPC.Party ToRichParty()
75 | {
76 | //We are not a valid party
77 | if (string.IsNullOrEmpty(identifer))
78 | return null;
79 |
80 | //return the party
81 | return new DiscordRPC.Party()
82 | {
83 | ID = identifer,
84 | Max = maxSize,
85 | Size = size
86 | };
87 | }
88 |
89 | ///
90 | /// Generates a random party identifier
91 | ///
92 | ///
93 | public static string GenerateRandomIdentifer()
94 | {
95 | const string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
96 | const int chunkSize = 16;
97 |
98 | var builder = new System.Text.StringBuilder();
99 | for (int i = 0; i < 128; i++)
100 | {
101 | if (i > 0 && i % chunkSize == 0)
102 | {
103 | builder.Append("-");
104 | }
105 | else
106 | {
107 | char c = valid[Random.Range(0, valid.Length)];
108 | builder.Append(c);
109 | }
110 | }
111 |
112 | return builder.ToString();
113 | }
114 | }
115 | }
--------------------------------------------------------------------------------
/Runtime/Presence/Party.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: fad1ad43cdf18904fa5a85e3bf3447fe
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/Presence/Presence.cs:
--------------------------------------------------------------------------------
1 | using Lachee.Discord.Attributes;
2 | using UnityEngine;
3 |
4 | namespace Lachee.Discord
5 | {
6 | [System.Obsolete("The word Discord has been removed from types", true)]
7 | public sealed class DiscordPresence { }
8 |
9 | [System.Serializable]
10 | public sealed class Presence
11 | {
12 | [Header("Basic Details")]
13 |
14 | ///
15 | /// The details about the game. Appears underneath the game name
16 | ///
17 | [CharacterLimit(128)]
18 | [Tooltip("The details about the game")]
19 | public string details = "Playing a game";
20 |
21 | ///
22 | /// The current state of the game (In Game, In Menu etc). Appears next to the party size
23 | ///
24 | [CharacterLimit(128)]
25 | [Tooltip("The current state of the game (In Game, In Menu). It appears next to the party size.")]
26 | public string state = "In Game";
27 |
28 | [Header("Time Details")]
29 |
30 | ///
31 | /// The time the game started. 0 if the game hasn't started
32 | ///
33 | [Tooltip("The time the game started. Leave as 0 if the game has not yet started.")]
34 | public Timestamp startTime = 0;
35 |
36 | ///
37 | /// The time the game will end in. 0 to ignore endtime.
38 | ///
39 | [Tooltip("Time the game will end. Leave as 0 to ignore it.")]
40 | public Timestamp endTime = 0;
41 |
42 | [Header("Presentation Details")]
43 |
44 | ///
45 | /// The images used for the presence.
46 | ///
47 | [Tooltip("The images used for the presence")]
48 | public Asset smallAsset;
49 | public Asset largeAsset;
50 |
51 | [Header("Button Details")]
52 |
53 | ///
54 | /// The buttons used for the presence.
55 | ///
56 | [Tooltip("The buttons used for the presence")]
57 | public Button[] buttons;
58 |
59 | [Header("Party Details")]
60 |
61 | ///
62 | /// The current party
63 | ///
64 | [Tooltip("The current party. Identifier must not be empty")]
65 | public Party party = new Party("", 0, 0);
66 |
67 | ///
68 | /// The current secrets for the join / spectate feature.
69 | ///
70 | [Tooltip("The current secrets for the join / spectate feature.")]
71 | public Secrets secrets = new Secrets();
72 |
73 | ///
74 | /// Creates a new Presence object
75 | ///
76 | public Presence() { }
77 |
78 | ///
79 | /// Creats a new Presence object, copying values of the Rich Presence
80 | ///
81 | /// The rich presence, often received by discord.
82 | public Presence(DiscordRPC.BaseRichPresence presence)
83 | {
84 | if (presence != null)
85 | {
86 | this.state = presence.State;
87 | this.details = presence.Details;
88 |
89 | this.party = presence.HasParty() ? new Party(presence.Party) : new Party();
90 | this.secrets = presence.HasSecrets() ? new Secrets(presence.Secrets) : new Secrets();
91 |
92 | if (presence.HasAssets())
93 | {
94 | this.smallAsset = new Asset()
95 | {
96 | image = presence.Assets.SmallImageKey,
97 | tooltip = presence.Assets.SmallImageText,
98 | snowflake = presence.Assets.SmallImageID.GetValueOrDefault(0)
99 | };
100 |
101 |
102 | this.largeAsset = new Asset()
103 | {
104 | image = presence.Assets.LargeImageKey,
105 | tooltip = presence.Assets.LargeImageText,
106 | snowflake = presence.Assets.LargeImageID.GetValueOrDefault(0)
107 | };
108 | }
109 | else
110 | {
111 | this.smallAsset = new Asset();
112 | this.largeAsset = new Asset();
113 | }
114 |
115 | if (presence.HasTimestamps())
116 | {
117 | //This could probably be made simpler
118 | this.startTime = presence.Timestamps.Start.HasValue ? new Timestamp((long)presence.Timestamps.StartUnixMilliseconds.Value) : Timestamp.Invalid;
119 | this.endTime = presence.Timestamps.End.HasValue ? new Timestamp((long)presence.Timestamps.EndUnixMilliseconds.Value) : Timestamp.Invalid;
120 | }
121 | }
122 | else
123 | {
124 | this.state = "";
125 | this.details = "";
126 | this.party = new Party();
127 | this.secrets = new Secrets();
128 | this.smallAsset = new Asset();
129 | this.largeAsset = new Asset();
130 | this.startTime = Timestamp.Invalid;
131 | this.endTime = Timestamp.Invalid;
132 | }
133 |
134 | this.buttons = new Button[0];
135 | }
136 |
137 | public Presence(DiscordRPC.RichPresence presence)
138 | : this((DiscordRPC.BaseRichPresence)presence)
139 | {
140 | if (presence != null)
141 | {
142 | if (presence.HasButtons())
143 | {
144 | this.buttons = new Button[presence.Buttons.Length];
145 |
146 | for (int i = 0; i < presence.Buttons.Length; i++)
147 | {
148 | this.buttons[i] = new Button()
149 | {
150 | label = presence.Buttons[i].Label,
151 | url = presence.Buttons[i].Url
152 | };
153 | }
154 | }
155 | else
156 | {
157 | this.buttons = new Button[0];
158 | }
159 | }
160 | }
161 |
162 | ///
163 | /// Converts this object into a new instance of a rich presence, ready to be sent to the discord client.
164 | ///
165 | /// A new instance of a rich presence, ready to be sent to the discord client.
166 | public DiscordRPC.RichPresence ToRichPresence()
167 | {
168 | var presence = new DiscordRPC.RichPresence();
169 | presence.State = this.state;
170 | presence.Details = this.details;
171 |
172 | presence.Party = !this.party.IsEmpty() ? this.party.ToRichParty() : null;
173 | presence.Secrets = !this.secrets.IsEmpty() ? this.secrets.ToRichSecrets() : null;
174 |
175 | if ((smallAsset != null && !smallAsset.IsEmpty()) || (largeAsset != null && !largeAsset.IsEmpty()))
176 | {
177 | presence.Assets = new DiscordRPC.Assets()
178 | {
179 | SmallImageKey = smallAsset.image,
180 | SmallImageText = smallAsset.tooltip,
181 |
182 | LargeImageKey = largeAsset.image,
183 | LargeImageText = largeAsset.tooltip
184 | };
185 | }
186 |
187 | if (startTime.IsValid() || endTime.IsValid())
188 | {
189 | presence.Timestamps = new DiscordRPC.Timestamps();
190 | if (startTime.IsValid()) presence.Timestamps.Start = startTime.GetDateTime();
191 | if (endTime.IsValid()) presence.Timestamps.End = endTime.GetDateTime();
192 | }
193 |
194 | if (buttons.Length > 0)
195 | {
196 | presence.Buttons = new DiscordRPC.Button[buttons.Length];
197 |
198 | for (int i = 0; i < buttons.Length; i++)
199 | {
200 | presence.Buttons[i] = new DiscordRPC.Button
201 | {
202 | Label = buttons[i].label,
203 | Url = buttons[i].url
204 | };
205 | }
206 | }
207 |
208 | return presence;
209 | }
210 |
211 | public static explicit operator DiscordRPC.RichPresence(Presence presence)
212 | {
213 | return presence.ToRichPresence();
214 | }
215 |
216 | public static explicit operator Presence(DiscordRPC.RichPresence presence)
217 | {
218 | return new Presence(presence);
219 | }
220 | }
221 | }
--------------------------------------------------------------------------------
/Runtime/Presence/Presence.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: af7f245f8d5b833478fd90246d8d7168
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/Presence/Secrets.cs:
--------------------------------------------------------------------------------
1 | using Lachee.Discord.Attributes;
2 | using UnityEngine;
3 |
4 | namespace Lachee.Discord
5 | {
6 | [System.Obsolete("The word Discord has been removed from types", true)]
7 | public struct DiscordSecrets { }
8 |
9 | [System.Serializable]
10 | public struct Secrets
11 | {
12 | ///
13 | /// The secret data that will tell the client how to connect to the game to play. This could be a unique identifier for a fancy match maker or player id, lobby id, etc.
14 | /// It is recommended to encrypt this information so its hard for people to replicate it.
15 | /// Do NOT just use the IP address in this. That is a bad practice and can leave your players vulnerable!
16 | ///
17 | /// Max Length of 128 Bytes
18 | ///
19 | [CharacterLimit(128)]
20 | [Tooltip("The secret data that will tell the client how to connect to the game to play. This could be a unique identifier for a fancy match maker or player id, lobby id, etc.")]
21 | public string joinSecret;
22 |
23 |
24 | ///
25 | /// The secret data that will tell the client how to connect to the game to spectate. This could be a unique identifier for a fancy match maker or player id, lobby id, etc.
26 | /// It is recommended to encrypt this information so its hard for people to replicate it.
27 | /// Do NOT just use the IP address in this. That is a bad practice and can leave your players vulnerable!
28 | ///
29 | /// Max Length of 128 Bytes
30 | ///
31 | [CharacterLimit(128)]
32 | [Tooltip("The secret data that will tell the client how to connect to the game to spectate. This could be a unique identifier for a fancy match maker or player id, lobby id, etc.")]
33 | public string spectateSecret;
34 |
35 | ///
36 | /// Creates new instances of the secrets, using the as the base.
37 | ///
38 | /// The base to use the values from
39 | public Secrets(DiscordRPC.Secrets secrets)
40 | {
41 | this.joinSecret = secrets.JoinSecret;
42 | this.spectateSecret = secrets.SpectateSecret;
43 | }
44 |
45 |
46 | ///
47 | /// Is the secret object empty?
48 | ///
49 | ///
50 | public bool IsEmpty()
51 | {
52 | return string.IsNullOrEmpty(joinSecret) && string.IsNullOrEmpty(spectateSecret);
53 | }
54 |
55 | ///
56 | /// Converts this object into the DiscordRPC equivilent.
57 | ///
58 | ///
59 | public DiscordRPC.Secrets ToRichSecrets()
60 | {
61 | if (IsEmpty()) return null;
62 | return new DiscordRPC.Secrets()
63 | {
64 | JoinSecret = joinSecret,
65 | SpectateSecret = spectateSecret
66 | };
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/Runtime/Presence/Secrets.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: fbd2a77a49969eb4b8328cd569b4ff3f
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/Presence/Timestamp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | namespace Lachee.Discord
5 | {
6 | [System.Obsolete("The word Discord has been removed from types", true)]
7 | public sealed class DiscordTimestamp { }
8 |
9 | ///
10 | /// A special time class that can convert all manners of time into timestamps.
11 | ///
12 | [System.Serializable]
13 | public sealed class Timestamp
14 | {
15 | ///
16 | /// Representation of a invalid timestamp (unix epoch of 0 seconds).
17 | ///
18 | public static readonly Timestamp Invalid = new Timestamp(0L);
19 |
20 | ///
21 | /// The linux epoch of the timestamp. Use conversion methods such as to convert the time into unity relative times.
22 | /// This is used for implicit casting into a
23 | ///
24 | [Tooltip("Unix Epoch Timestamp")]
25 | public long timestamp = 0;
26 |
27 | ///
28 | /// Creates a new stamp of the current time.
29 | ///
30 | public Timestamp()
31 | : this(DateTime.UtcNow) { }
32 |
33 | ///
34 | /// Creates a new stamp with the supplied datetime
35 | ///
36 | /// The DateTime
37 | public Timestamp(DateTime time)
38 | : this(ToUnixMilliseconds(DateTime.UtcNow)) { }
39 |
40 | ///
41 | /// Creates a new stamp with the specified unix epoch
42 | ///
43 | /// The time in unix epoch milliseconds
44 | public Timestamp(long timestamp)
45 | {
46 | this.timestamp = timestamp;
47 | }
48 |
49 | ///
50 | /// Creates a new stamp that is relative to the Unity Startup time, where "now" is equal too .
51 | ///
52 | /// The time relative to
53 | public Timestamp(float time)
54 | {
55 | //Calculate the difference
56 | float diff = time - UnityEngine.Time.realtimeSinceStartup;
57 |
58 | //Convert to timespan and then to unix epoch
59 | TimeSpan timespan = TimeSpan.FromSeconds(diff); //Convert the difference to a TimeSpan for easier maths
60 | timestamp = ToUnixMilliseconds(DateTime.UtcNow + timespan); //Add the difference to the current time
61 | }
62 |
63 | ///
64 | /// Converts the timestamp into a
65 | /// This is used for implicit conversion into a
66 | ///
67 | ///
68 | public DateTime GetDateTime()
69 | {
70 | return FromUnixMilliseconds(timestamp);
71 | }
72 |
73 | ///
74 | /// Converts the timestamp into the number of seconds since the startup of the game (Unity relative), where "now" is equal too .
75 | /// This is used for implicit convertion into a .
76 | ///
77 | ///
78 | public float GetTime()
79 | {
80 | DateTime time = GetDateTime();
81 | TimeSpan timespan = time - DateTime.UtcNow;
82 | return UnityEngine.Time.realtimeSinceStartup + (float)timespan.TotalSeconds;
83 | }
84 |
85 | ///
86 | /// Checks if the timestamp is valid (above 0 seconds relative to unix epoch).
87 | ///
88 | /// Returns true if the timestamp is a non-zero epoch.
89 | public bool IsValid() { return this.timestamp > 0; }
90 |
91 | ///
92 | /// Adds seconds onto the timestamp.
93 | ///
94 | /// The number of seconds to add
95 | /// Returns the same timestamp object.
96 | public Timestamp AddSeconds(int seconds)
97 | {
98 | timestamp += seconds;
99 | return this;
100 | }
101 |
102 | ///
103 | /// Adds minutes onto the timestamp.
104 | ///
105 | /// The number of minutes to add
106 | /// Returns the same timestamp object.
107 | public Timestamp AddMinutes(int minutes)
108 | {
109 | return AddSeconds(minutes * 60);
110 | }
111 |
112 | ///
113 | /// Adds minutes onto the timestamp, rounding off to the nearest second.
114 | ///
115 | /// The fraction of minutes to add
116 | /// Returns the same timestamp object.
117 | public Timestamp AddMinutes(float minutes)
118 | {
119 | //Convert the time into a integer form
120 | int mins = Mathf.FloorToInt(minutes);
121 | int secs = Mathf.RoundToInt((minutes - mins) * 60);
122 |
123 | return AddMinutes(mins).AddSeconds(secs);
124 | }
125 |
126 | ///
127 | /// Adds hours onto the timestamp.
128 | ///
129 | /// The number of hours to add
130 | /// Returns the same timestamp object
131 | public Timestamp AddHours(int hours)
132 | {
133 | return AddMinutes(hours * 60);
134 | }
135 |
136 | ///
137 | /// Adds hours onto the timestamp, rounding off to the nearest second.
138 | ///
139 | /// The fraction of hours to add
140 | /// Returns the same timestamp object.
141 | public Timestamp AddHours(float hours)
142 | {
143 | int h = Mathf.FloorToInt(hours);
144 | float m = (hours - h) * 60f;
145 | return AddHours(h).AddMinutes(m);
146 | }
147 |
148 |
149 | #region Value Conversions
150 | ///
151 | /// Casts the timestamp into a unix epoch count of seconds.
152 | ///
153 | /// The timestamp
154 | public static implicit operator long(Timestamp stamp)
155 | {
156 | return stamp.timestamp;
157 | }
158 | ///
159 | /// Converts the timestamp into a unity epoch (start of the game time as origin) count of seconds, where is now.
160 | ///
161 | /// The timestamp
162 | public static implicit operator float(Timestamp stamp)
163 | {
164 | return stamp.GetTime();
165 | }
166 | ///
167 | /// Converts the timestamp into a representation.
168 | ///
169 | /// The timestamp
170 | public static implicit operator DateTime(Timestamp stamp)
171 | {
172 | return stamp.GetDateTime();
173 | }
174 | #endregion
175 |
176 | #region Stamp Conversions
177 | ///
178 | /// Casts a unixh epoch count of seconds into a timestamp
179 | ///
180 | /// The time in milliseconds
181 | public static implicit operator Timestamp(long time)
182 | {
183 | return new Timestamp(time);
184 | }
185 |
186 | ///
187 | /// Converts the into a timestamp
188 | ///
189 | /// The time
190 | public static implicit operator Timestamp(DateTime time)
191 | {
192 | return new Timestamp(time);
193 | }
194 | ///
195 | /// Converts a unity epoch (start of game time as origin) count of seconds (where is now) into a timestamp.
196 | ///
197 | /// The time
198 | public static implicit operator Timestamp(float time)
199 | {
200 | return new Timestamp(time);
201 | }
202 | #endregion
203 |
204 | ///
205 | /// Converts a Unix Epoch time into a .
206 | ///
207 | /// The time in milliseconds since 1970 / 01 / 01
208 | ///
209 | public static DateTime FromUnixMilliseconds(long unixTime)
210 | {
211 | var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
212 | return epoch.AddMilliseconds(Convert.ToDouble(unixTime));
213 | }
214 |
215 | ///
216 | /// Converts a into a Unix Epoch time (in milliseconds).
217 | ///
218 | /// The datetime to convert
219 | ///
220 | public static long ToUnixMilliseconds(DateTime date)
221 | {
222 | var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
223 | return Convert.ToInt64((date - epoch).TotalMilliseconds);
224 | }
225 | }
226 | }
--------------------------------------------------------------------------------
/Runtime/Presence/Timestamp.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 22be56012353e8c4db5592836fcecb6f
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/Presence/User.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections;
4 | using System.IO;
5 | using UnityEngine;
6 |
7 | #if UNITY_2017_4_OR_NEWER
8 | using UnityEngine.Networking;
9 | #endif
10 |
11 | namespace Lachee.Discord
12 | {
13 | [System.Serializable]
14 | public sealed class User
15 | {
16 | ///
17 | /// Caching Level. This is a flag.
18 | ///
19 | [System.Flags]
20 | public enum CacheLevelFlag
21 | {
22 | /// Disable all caching
23 | None = 0,
24 |
25 | /// Caches avatars by user id (required for caching to work).
26 | UserId = 1,
27 |
28 | /// Caches the avatars by avatar hash.
29 | Hash = 3, //UserId | 2,
30 |
31 | /// Caches the avatars by size. If off, only the largest size is stored.
32 | Size = 5, //UserId | 4
33 | }
34 |
35 | ///
36 | /// The current location of the avatar caches
37 | ///
38 | public static string CacheDirectory = null;
39 |
40 | ///
41 | /// The caching level used by the avatar functions. Note that the cache is never cleared. The cache level will help mitigate exessive file counts.
42 | /// will cause no images to be cached and will be downloaded everytime they are fetched.
43 | /// will cache images based of their hash. Without this, the avatar will likely stay the same forever.
44 | /// will cache images based of their size. Useful, but may result in multiples of the same file. Disabling this will cause all files to be x512.
45 | ///
46 | public static CacheLevelFlag CacheLevel = CacheLevelFlag.None;
47 |
48 | ///
49 | /// The format to download and cache avatars in. By default, PNG is used.
50 | ///
51 | public static DiscordAvatarFormat AvatarFormat { get; set; }
52 |
53 |
54 | private DiscordRPC.User _user;
55 |
56 | ///
57 | /// The username of the Discord user
58 | ///
59 | public string username => _user?.Username;
60 |
61 | ///
62 | /// The display name of the user
63 | ///
64 | /// This will be empty if the user has not set a global display name.
65 | public string displayName => _user?.DisplayName;
66 |
67 | ///
68 | /// The discriminator of the user.
69 | ///
70 | /// If the user has migrated to unique a , the discriminator will always be 0.
71 | [Obsolete("Discord no longer uses discriminators.")]
72 | public int discriminator => (_user?.Discriminator).GetValueOrDefault();
73 |
74 | ///
75 | /// The discriminator in a nicely formatted string.
76 | ///
77 | [Obsolete("Discord no longer uses discriminators.")]
78 | public string discrim { get { return "#" + discriminator.ToString("D4"); } }
79 |
80 | ///
81 | /// The unique snowflake ID of the Discord user
82 | ///
83 | public ulong ID => (_user?.ID).GetValueOrDefault();
84 |
85 | ///
86 | /// The hash of the users avatar. Used to generate the URL's
87 | ///
88 | public string avatarHash => (_user?.Avatar);
89 |
90 | ///
91 | /// The current avatar cache. Will return null until is called.
92 | ///
93 | public Texture2D avatar { get; private set; }
94 |
95 | ///
96 | /// The size of the currently cached avatar
97 | ///
98 | public DiscordAvatarSize cacheSize { get; private set; }
99 |
100 | ///
101 | /// The format of the currently cached avatar
102 | ///
103 | public DiscordAvatarFormat cacheFormat { get; private set; }
104 |
105 | ///
106 | /// The current URL for the discord avatars
107 | ///
108 | private string cdnEndpoint => _user?.CdnEndpoint;
109 |
110 | #if UNITY_EDITOR
111 | #pragma warning disable 0414
112 | [HideInInspector]
113 | [SerializeField]
114 | private bool e_foldout = true;
115 | #pragma warning restore 0414
116 | #endif
117 |
118 | public User(DiscordRPC.User user)
119 | {
120 | _user = user;
121 | }
122 |
123 | ///
124 | /// An event that is triggered when the avatar finishes downloading.
125 | ///
126 | /// The user the avatar belongs too
127 | /// The avatar that was downloaded
128 | public delegate void AvatarDownloadCallback(User user, Texture2D avatar);
129 |
130 | ///
131 | /// Gets the user avatar as a Texture2D and starts it with the supplied monobehaviour. It will first check the cache if the image exists, if it does it will return the image. Otherwise it will download the image from Discord and store it in the cache, calling the callback once done.
132 | ///
133 | /// The target size of the avatar. Default is 128x128
134 | /// The callback for when the texture completes. Default is no-callback, but its highly recommended to use a callback
135 | ///
136 | public void GetAvatar(DiscordAvatarSize size = DiscordAvatarSize.x128, AvatarDownloadCallback callback = null)
137 | {
138 | DiscordManager.current.StartCoroutine(GetAvatarCoroutine(size, callback));
139 | }
140 |
141 | ///
142 | /// Gets the user avatar as a Texture2D as a enumerator. It will first check the cache if the image exists, if it does it will return the image. Otherwise it will download the image from Discord and store it in the cache, calling the callback once done.
143 | /// If has set, then the size will be ignored and will be used instead.
144 | /// If is , then no files will be written for cache.
145 | ///
146 | /// The target size of the avatar. Default is 128x128
147 | /// The callback for when the texture completes. Default is no-callback, but its highly recommended to use a callback
148 | ///
149 | public IEnumerator GetAvatarCoroutine(DiscordAvatarSize size = DiscordAvatarSize.x128, AvatarDownloadCallback callback = null)
150 | {
151 | if (avatar != null)
152 | {
153 | //Execute the callback (if any)
154 | if (callback != null)
155 | callback.Invoke(this, avatar);
156 |
157 | //Stop here, we did all we need to do
158 | yield break;
159 | }
160 |
161 | if (string.IsNullOrEmpty(avatarHash))
162 | {
163 | yield return GetDefaultAvatarCoroutine(size, callback);
164 | }
165 | else
166 | {
167 | //Prepare the cache path
168 | string path = null;
169 |
170 | //Build the formatting
171 | if (CacheLevel != CacheLevelFlag.None)
172 | {
173 | //Update the default cache just incase its null
174 | SetupDefaultCacheDirectory();
175 |
176 | string format = "{0}";
177 | if ((CacheLevel & CacheLevelFlag.Hash) == CacheLevelFlag.Hash) format += "-{1}";
178 | if ((CacheLevel & CacheLevelFlag.Size) == CacheLevelFlag.Size) format += "{2}"; else size = DiscordAvatarSize.x512;
179 |
180 | //Generate the path name
181 | string filename = string.Format(format + ".{3}", ID, avatarHash, size.ToString(), User.AvatarFormat.ToString().ToLowerInvariant());
182 | path = Path.Combine(CacheDirectory, filename);
183 | Debug.Log("Cache: " + path);
184 | }
185 |
186 | //The holder texture is null, so we should create new one
187 | Texture2D avatarTexture = new Texture2D((int)size, (int)size, TextureFormat.RGBA32, false);
188 |
189 | //Check if the file exists and we have caching enabled
190 | if (CacheLevel != CacheLevelFlag.None && File.Exists(path))
191 | {
192 | //Load the image
193 | var bytes = File.ReadAllBytes(path);
194 | avatarTexture.LoadImage(bytes);
195 | }
196 | else
197 | {
198 | #if UNITY_2017_4_OR_NEWER
199 | using (UnityWebRequest req = UnityWebRequestTexture.GetTexture(GetAvatarURL(User.AvatarFormat, size)))
200 | {
201 | //Download the texture
202 | yield return req.SendWebRequest();
203 |
204 | #if UNITY_2020_1_OR_NEWER
205 | if (req.result == UnityWebRequest.Result.ConnectionError || req.result == UnityWebRequest.Result.ProtocolError)
206 | #else
207 | if (req.isNetworkError || req.isHttpError)
208 | #endif
209 | {
210 | //Report errors
211 | Debug.LogError("Failed to download user avatar: " + req.error);
212 | }
213 | else
214 | {
215 | //Update the avatar
216 | avatarTexture = DownloadHandlerTexture.GetContent(req);
217 | }
218 | }
219 | #else
220 | using (WWW www = new WWW(GetAvatarURL(DiscordUser.AvatarFormat, size)))
221 | {
222 | //Download the texture
223 | yield return www;
224 |
225 | //Update the holder
226 | www.LoadImageIntoTexture(avatarTexture);
227 | }
228 | #endif
229 | }
230 |
231 | //Apply our avatar and update our cache
232 | if (avatarTexture != null)
233 | {
234 | CacheAvatarTexture(avatarTexture, path);
235 | avatar = avatarTexture;
236 | cacheFormat = User.AvatarFormat;
237 | cacheSize = size;
238 |
239 | //Execute the callback (if any)
240 | if (callback != null)
241 | callback.Invoke(this, avatarTexture);
242 | }
243 | }
244 | }
245 |
246 | /// Caches the avatar
247 | private void CacheAvatarTexture(Texture2D texture, string path)
248 | {
249 | //Encode and cache the files
250 | if (CacheLevel != CacheLevelFlag.None)
251 | {
252 | //Create the directory if it doesnt already exist
253 | if (!Directory.Exists(CacheDirectory))
254 | Directory.CreateDirectory(CacheDirectory);
255 |
256 | //Encode the image
257 | byte[] bytes;
258 | switch (User.AvatarFormat)
259 | {
260 | default:
261 | case DiscordAvatarFormat.PNG:
262 | bytes = texture.EncodeToPNG();
263 | break;
264 |
265 | case DiscordAvatarFormat.JPEG:
266 | bytes = texture.EncodeToJPG();
267 | break;
268 | }
269 |
270 | //Save the image
271 | File.WriteAllBytes(path, bytes);
272 | }
273 | }
274 |
275 |
276 | ///
277 | /// Gets the default avatar for the given user. Will check the cache first, and if none are available it will then download the default from discord.
278 | /// If has set, then the size will be ignored and will be used instead.
279 | /// If is , then no files will be written for cache.
280 | ///
281 | /// The size of the target avatar
282 | /// The callback that will be made when the picture finishes downloading.
283 | ///
284 | ///
285 | public IEnumerator GetDefaultAvatarCoroutine(DiscordAvatarSize size = DiscordAvatarSize.x128, AvatarDownloadCallback callback = null)
286 | {
287 | //Calculate the discrim number and prepare the cache path
288 | string path = null;
289 | int index = (int)((ID >> 22) % 6);
290 |
291 | #pragma warning disable CS0618 // Disable the obsolete warning as we know the discriminator is obsolete and we are validating it here.
292 | if (discriminator > 0)
293 | index = discriminator % 5;
294 | #pragma warning restore CS0618
295 |
296 | //Update the default cache just incase its null
297 | if (CacheLevel != CacheLevelFlag.None)
298 | {
299 | //Setup the dir
300 | SetupDefaultCacheDirectory();
301 |
302 | //should we cache the size?
303 | bool cacheSize = (CacheLevel & CacheLevelFlag.Size) == CacheLevelFlag.Size;
304 | if (!cacheSize) size = DiscordAvatarSize.x512;
305 |
306 | string filename = string.Format("default-{0}{1}.png", index, cacheSize ? size.ToString() : "");
307 | path = Path.Combine(CacheDirectory, filename);
308 | }
309 |
310 | //The holder texture is null, so we should create new one
311 | Texture2D avatarTexture = new Texture2D((int)size, (int)size, TextureFormat.RGBA32, false);
312 |
313 | //Check if the file exists
314 | if (CacheLevel != CacheLevelFlag.None && File.Exists(path))
315 | {
316 | //Load the image
317 | byte[] bytes = File.ReadAllBytes(path);
318 | avatarTexture.LoadImage(bytes);
319 | }
320 | else
321 | {
322 | string url = string.Format("https://{0}/embed/avatars/{1}.png?size={2}", cdnEndpoint, index, (int)size);
323 |
324 | #if UNITY_2017_4_OR_NEWER
325 | using (UnityWebRequest req = UnityWebRequestTexture.GetTexture(url))
326 | {
327 | //Download the texture
328 | yield return req.SendWebRequest();
329 | #if UNITY_2020_1_OR_NEWER
330 | if (req.result == UnityWebRequest.Result.ConnectionError || req.result == UnityWebRequest.Result.ProtocolError)
331 | #else
332 | if (req.isNetworkError || req.isHttpError)
333 | #endif
334 | {
335 | //Report errors
336 | Debug.LogError("Failed to download default avatar: " + req.error);
337 | }
338 | else
339 | {
340 | //Update the avatar
341 | avatarTexture = DownloadHandlerTexture.GetContent(req);
342 | }
343 | }
344 | #else
345 | using (WWW www = new WWW(url))
346 | {
347 | //Download the texture
348 | yield return www;
349 |
350 | //Update the holder
351 | www.LoadImageIntoTexture(avatarTexture);
352 | }
353 | #endif
354 | //We have been told to cache, so do so.
355 | if (CacheLevel != CacheLevelFlag.None)
356 | {
357 | //Create the directory if it doesnt already exist
358 | if (!Directory.Exists(CacheDirectory))
359 | Directory.CreateDirectory(CacheDirectory);
360 |
361 | byte[] bytes = avatarTexture.EncodeToPNG();
362 | File.WriteAllBytes(path, bytes);
363 | }
364 | }
365 |
366 | //Apply our avatar and update our cache
367 | avatar = avatarTexture;
368 | cacheFormat = DiscordAvatarFormat.PNG;
369 | cacheSize = size;
370 |
371 | //Execute the callback (if any)
372 | if (callback != null)
373 | callback.Invoke(this, avatar);
374 | }
375 |
376 | ///
377 | /// Updates the default directory for the cache
378 | ///
379 | private static void SetupDefaultCacheDirectory()
380 | {
381 | if (CacheDirectory == null)
382 | CacheDirectory = Application.dataPath + "/Discord Rpc/Cache";
383 | }
384 |
385 | ///
386 | /// Gets a URL that can be used to download the user's avatar. If the user has not yet set their avatar, it will return the default one that discord is using. The default avatar only supports the format.
387 | ///
388 | /// The format of the target avatar
389 | /// The optional size of the avatar you wish for.
390 | /// URL to the discord CDN for the particular avatar
391 | private string GetAvatarURL(DiscordAvatarFormat format, DiscordAvatarSize size)
392 | {
393 | return _user.GetAvatarURL(format == DiscordAvatarFormat.PNG ? DiscordRPC.User.AvatarFormat.PNG : DiscordRPC.User.AvatarFormat.JPEG, (DiscordRPC.User.AvatarSize)size);
394 | }
395 |
396 | ///
397 | /// Formats the user into a displayable format. If the user has a , then this will be used.
398 | /// If the user still has a discriminator, then this will return the form of `Username#Discriminator`.
399 | ///
400 | /// String of the user that can be used for display.
401 | public override string ToString()
402 | {
403 | if (_user == null) return "N/A";
404 | return _user.ToString();
405 | }
406 |
407 | ///
408 | /// Implicit casting from a DiscordRPC.User to a DiscordUser
409 | ///
410 | ///
411 | public static implicit operator User(DiscordRPC.User user) { return new User(user); }
412 | public static implicit operator DiscordRPC.User(User user) { return user._user; }
413 |
414 | public override int GetHashCode()
415 | {
416 | return this.ID.GetHashCode() ^ 7;
417 | }
418 |
419 | public override bool Equals(object obj)
420 | {
421 | if (obj is User)
422 | return this.ID == ((User)obj).ID;
423 |
424 | return false;
425 | }
426 | }
427 |
428 | ///
429 | /// The format of the discord avatars in the cache
430 | ///
431 | public enum DiscordAvatarFormat
432 | {
433 | ///
434 | /// Portable Network Graphics format (.png)
435 | /// Losses format that supports transparent avatars. Most recommended for stationary formats with wide support from many libraries.
436 | ///
437 | PNG,
438 |
439 | ///
440 | /// Joint Photographic Experts Group format (.jpeg)
441 | /// The format most cameras use. Lossy and does not support transparent avatars.
442 | ///
443 | JPEG
444 | }
445 |
446 | ///
447 | /// Possible square sizes of avatars.
448 | ///
449 | public enum DiscordAvatarSize
450 | {
451 | /// 16 x 16 pixels.
452 | x16 = 16,
453 | /// 32 x 32 pixels.
454 | x32 = 32,
455 | /// 64 x 64 pixels.
456 | x64 = 64,
457 | /// 128 x 128 pixels.
458 | x128 = 128,
459 | /// 256 x 256 pixels.
460 | x256 = 256,
461 | /// 512 x 512 pixels.
462 | x512 = 512,
463 | /// 1024 x 1024 pixels.
464 | x1024 = 1024,
465 | /// 2048 x 2048 pixels.
466 | x2048 = 2048
467 | }
468 |
469 | ///
470 | /// Collection of extensions to the class.
471 | ///
472 | public static class DiscordUserExtension
473 | {
474 | ///
475 | /// Gets the user avatar as a Texture2D and starts it with the supplied monobehaviour. It will first check the cache if the image exists, if it does it will return the image. Otherwise it will download the image from Discord and store it in the cache, calling the callback once done.
476 | /// An alias of and will return the new instance.
477 | ///
478 | /// The target size of the avatar. Default is 128x128
479 | /// The callback for when the texture completes. Default is no-callback, but its highly recommended to use a callback
480 | /// Returns the generated for this object.
481 | public static User GetAvatar(this DiscordRPC.User user,DiscordAvatarSize size = DiscordAvatarSize.x128, User.AvatarDownloadCallback callback = null)
482 | {
483 | var du = new User(user);
484 | du.GetAvatar(size, callback);
485 | return du;
486 | }
487 |
488 | ///
489 | /// Gets the user avatar as a Texture2D as a enumerator. It will first check the cache if the image exists, if it does it will return the image. Otherwise it will download the image from Discord and store it in the cache, calling the callback once done.
490 | /// An alias of and will return the new instance in the callback.
491 | ///
492 | /// The target size of the avatar. Default is 128x128
493 | /// The callback for when the texture completes. Default is no-callback, but its highly recommended to use a callback
494 | ///
495 | public static IEnumerator GetAvatarCoroutine(this DiscordRPC.User user, DiscordAvatarSize size = DiscordAvatarSize.x128, User.AvatarDownloadCallback callback = null)
496 | {
497 | var du = new User(user);
498 | return du.GetDefaultAvatarCoroutine(size, callback);
499 | }
500 |
501 | ///
502 | /// Gets the default avatar for the given user. Will check the cache first, and if none are available it will then download the default from discord.
503 | /// An alias of and will return the new instance in the callback.
504 | ///
505 | /// The size of the target avatar
506 | /// The callback that will be made when the picture finishes downloading.
507 | ///
508 | public static IEnumerator GetDefaultAvatarCoroutine(this DiscordRPC.User user, DiscordAvatarSize size = DiscordAvatarSize.x128, User.AvatarDownloadCallback callback = null)
509 | {
510 | var du = new User(user);
511 | return du.GetDefaultAvatarCoroutine(size, callback);
512 | }
513 | }
514 | }
--------------------------------------------------------------------------------
/Runtime/Presence/User.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3adb9074488ff7d4b986add5005df8fc
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/com.lachee.discordrpc.runtime.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.lachee.discordrpc.runtime",
3 | "references": [],
4 | "optionalUnityReferences": [],
5 | "includePlatforms": [],
6 | "excludePlatforms": [],
7 | "allowUnsafeCode": false
8 | }
--------------------------------------------------------------------------------
/Runtime/com.lachee.discordrpc.runtime.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: dbe5ccece09868a4db171d8cd0086c18
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Samples~/Rich Presence.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 29b8d30d888719a4ca2fd2c5ec2fb761
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Samples~/Rich Presence/ConstantRotate.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using UnityEngine;
4 |
5 | namespace DiscordRPC.Examples
6 | {
7 | public class ConstantRotate : MonoBehaviour
8 | {
9 |
10 | public Vector3 rotation;
11 |
12 | // Update is called once per frame
13 | void Update()
14 | {
15 | transform.Rotate(rotation);
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Samples~/Rich Presence/ConstantRotate.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 835ec75000b58cf4aba592fb846a629b
3 | timeCreated: 1521367966
4 | licenseType: Free
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/Samples~/Rich Presence/Example_ReadyListener.cs:
--------------------------------------------------------------------------------
1 | using Lachee.Discord;
2 | using Lachee.Discord.Events;
3 | using System.Collections;
4 | using System.Collections.Generic;
5 | using UnityEngine;
6 |
7 | namespace DiscordRPC.Examples.RichPresence
8 | {
9 | public class Example_ReadyListener : MonoBehaviour
10 | {
11 | void Start()
12 | {
13 | // This method is only required for 2018 or below.
14 | // Since 2019, Unity can serialize generics
15 | DiscordManager.current.OnReady.AddListener(OnReady);
16 | }
17 |
18 | public void OnReady(ReadyEvent evt)
19 | {
20 | Debug.Log("Received Ready!");
21 | evt.user.GetAvatar(DiscordAvatarSize.x1024, (user, texture) =>
22 | {
23 | var renderer = GetComponent();
24 | renderer.material.mainTexture = texture;
25 | });
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/Samples~/Rich Presence/Example_ReadyListener.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a6886d548845d24468b7160b582ef670
3 | timeCreated: 1535178489
4 | licenseType: Free
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/Samples~/Rich Presence/Example_RichPresence.cs:
--------------------------------------------------------------------------------
1 | using Lachee.Discord;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using UnityEngine;
5 | using UnityEngine.UI;
6 |
7 | namespace DiscordRPC.Examples.RichPresence
8 | {
9 | public class Example_RichPresence : MonoBehaviour
10 | {
11 |
12 | [Header("Details")]
13 | public InputField inputDetails, inputState;
14 |
15 | [Header("Time")]
16 | public Toggle inputStartTime;
17 | public InputField inputEndTime;
18 |
19 | [Header("Images")]
20 | public InputField inputLargeKey;
21 | public InputField inputLargeTooltip;
22 | public InputField inputSmallKey;
23 | public InputField inputSmallTooltip;
24 |
25 |
26 | public Presence presence;
27 |
28 | private void Start()
29 | {
30 | //Update the values
31 | UpdateFields(presence);
32 |
33 | //Register to a presence change
34 | DiscordManager.current.OnPresence.AddListener((message) =>
35 | {
36 | Debug.Log("Received a new presence! Current App: " + message.applicationID + ", " + message.name);
37 | UpdateFields(presence);
38 | });
39 | }
40 |
41 | public void SendPresence()
42 | {
43 | UpdatePresence();
44 | DiscordManager.current.SetPresence(presence);
45 | }
46 |
47 | public void UpdateFields(Presence presence)
48 | {
49 | if (presence == null)
50 | {
51 | return;
52 | }
53 |
54 | this.presence = presence;
55 | inputState.text = presence.state;
56 | inputDetails.text = presence.details;
57 |
58 |
59 | inputLargeTooltip.text = presence.largeAsset.tooltip;
60 | inputLargeKey.text = presence.largeAsset.image;
61 |
62 | inputSmallTooltip.text = presence.smallAsset.tooltip;
63 | inputSmallKey.text = presence.smallAsset.image;
64 | }
65 |
66 | public void UpdatePresence()
67 | {
68 | presence.state = inputState.text;
69 | presence.details = inputDetails.text;
70 | presence.startTime = inputStartTime.isOn ? new Timestamp(Time.realtimeSinceStartup) : Timestamp.Invalid;
71 |
72 | presence.largeAsset = new Asset()
73 | {
74 | image = inputLargeKey.text,
75 | tooltip = inputLargeTooltip.text
76 | };
77 | presence.smallAsset = new Asset()
78 | {
79 | image = inputSmallKey.text,
80 | tooltip = inputSmallTooltip.text
81 | };
82 |
83 | float duration = float.Parse(inputEndTime.text);
84 | presence.endTime = duration > 0 ? new Timestamp(Time.realtimeSinceStartup + duration) : Timestamp.Invalid;
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/Samples~/Rich Presence/Example_RichPresence.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ee9b415880f902147837a1db9af2150b
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Samples~/Rich Presence/Example_RichPresence.unity.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 799086f5f8ec62d4f9426712b2e14809
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Samples~/Rich Presence/Example_SetStatusButton.cs:
--------------------------------------------------------------------------------
1 | using Lachee.Discord;
2 | using Lachee.Discord.Events;
3 | using System.Collections;
4 | using System.Collections.Generic;
5 | using UnityEngine;
6 |
7 | namespace DiscordRPC.Examples.RichPresence
8 | {
9 | public class Example_SetStatusButton : MonoBehaviour
10 | {
11 | public UnityEngine.UI.Button button;
12 | public UnityEngine.UI.InputField input;
13 |
14 | void Start()
15 | {
16 | // This method is only required for 2018 or below.
17 | // Since 2019, Unity can serialize generics
18 | DiscordManager.current.OnReady.AddListener(OnReady);
19 | button.onClick.AddListener(OnClick);
20 | button.interactable = false;
21 | }
22 |
23 | public void OnReady(ReadyEvent evt)
24 | {
25 | button.interactable = true;
26 | }
27 |
28 | public void OnClick()
29 | {
30 | var presence = DiscordManager.current.CurrentPresence;
31 | presence.details = input.text;
32 | DiscordManager.current.SetPresence(presence);
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/Samples~/Rich Presence/Example_SetStatusButton.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 69e3792da7f6c8640a8e82dbfd081e47
3 | timeCreated: 1535178489
4 | licenseType: Free
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/Samples~/Rich Presence/Spinning Cubes.prefab:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!1001 &100100000
4 | Prefab:
5 | m_ObjectHideFlags: 1
6 | serializedVersion: 2
7 | m_Modification:
8 | m_TransformParent: {fileID: 0}
9 | m_Modifications: []
10 | m_RemovedComponents: []
11 | m_SourcePrefab: {fileID: 0}
12 | m_RootGameObject: {fileID: 1073555440304004}
13 | m_IsPrefabAsset: 1
14 | --- !u!1 &1041719462949384
15 | GameObject:
16 | m_ObjectHideFlags: 0
17 | m_CorrespondingSourceObject: {fileID: 0}
18 | m_PrefabInternal: {fileID: 100100000}
19 | serializedVersion: 6
20 | m_Component:
21 | - component: {fileID: 4729002408311522}
22 | - component: {fileID: 33564632248098672}
23 | - component: {fileID: 65047797416528598}
24 | - component: {fileID: 23054393140330284}
25 | - component: {fileID: 114115697624970392}
26 | m_Layer: 0
27 | m_Name: Baby Cube
28 | m_TagString: Untagged
29 | m_Icon: {fileID: 0}
30 | m_NavMeshLayer: 0
31 | m_StaticEditorFlags: 0
32 | m_IsActive: 1
33 | --- !u!1 &1073555440304004
34 | GameObject:
35 | m_ObjectHideFlags: 0
36 | m_CorrespondingSourceObject: {fileID: 0}
37 | m_PrefabInternal: {fileID: 100100000}
38 | serializedVersion: 6
39 | m_Component:
40 | - component: {fileID: 4162741165016952}
41 | m_Layer: 0
42 | m_Name: Spinning Cubes
43 | m_TagString: Untagged
44 | m_Icon: {fileID: 0}
45 | m_NavMeshLayer: 0
46 | m_StaticEditorFlags: 0
47 | m_IsActive: 1
48 | --- !u!1 &1680671491621748
49 | GameObject:
50 | m_ObjectHideFlags: 0
51 | m_CorrespondingSourceObject: {fileID: 0}
52 | m_PrefabInternal: {fileID: 100100000}
53 | serializedVersion: 6
54 | m_Component:
55 | - component: {fileID: 4545879213793466}
56 | - component: {fileID: 33966280038937740}
57 | - component: {fileID: 65423381411825540}
58 | - component: {fileID: 23317795868008346}
59 | - component: {fileID: 114491102716508916}
60 | m_Layer: 0
61 | m_Name: Baby Cube
62 | m_TagString: Untagged
63 | m_Icon: {fileID: 0}
64 | m_NavMeshLayer: 0
65 | m_StaticEditorFlags: 0
66 | m_IsActive: 1
67 | --- !u!1 &1750859589963992
68 | GameObject:
69 | m_ObjectHideFlags: 0
70 | m_CorrespondingSourceObject: {fileID: 0}
71 | m_PrefabInternal: {fileID: 100100000}
72 | serializedVersion: 6
73 | m_Component:
74 | - component: {fileID: 4782709639625668}
75 | - component: {fileID: 33051478306752160}
76 | - component: {fileID: 65227327944083028}
77 | - component: {fileID: 23165215184582192}
78 | - component: {fileID: 114194695389752790}
79 | - component: {fileID: 114780491621147760}
80 | m_Layer: 0
81 | m_Name: Daddy Cube
82 | m_TagString: Untagged
83 | m_Icon: {fileID: 0}
84 | m_NavMeshLayer: 0
85 | m_StaticEditorFlags: 0
86 | m_IsActive: 1
87 | --- !u!1 &1821574291237272
88 | GameObject:
89 | m_ObjectHideFlags: 0
90 | m_CorrespondingSourceObject: {fileID: 0}
91 | m_PrefabInternal: {fileID: 100100000}
92 | serializedVersion: 6
93 | m_Component:
94 | - component: {fileID: 4964883829626994}
95 | - component: {fileID: 33934235896704876}
96 | - component: {fileID: 65262898154187970}
97 | - component: {fileID: 23360437394164212}
98 | - component: {fileID: 114111543808520302}
99 | m_Layer: 0
100 | m_Name: Baby Cube
101 | m_TagString: Untagged
102 | m_Icon: {fileID: 0}
103 | m_NavMeshLayer: 0
104 | m_StaticEditorFlags: 0
105 | m_IsActive: 1
106 | --- !u!1 &1948494897459636
107 | GameObject:
108 | m_ObjectHideFlags: 0
109 | m_CorrespondingSourceObject: {fileID: 0}
110 | m_PrefabInternal: {fileID: 100100000}
111 | serializedVersion: 6
112 | m_Component:
113 | - component: {fileID: 4690729713261028}
114 | - component: {fileID: 33710475515543736}
115 | - component: {fileID: 65510122804828914}
116 | - component: {fileID: 23512684803758586}
117 | - component: {fileID: 114733796681027892}
118 | m_Layer: 0
119 | m_Name: Baby Cube
120 | m_TagString: Untagged
121 | m_Icon: {fileID: 0}
122 | m_NavMeshLayer: 0
123 | m_StaticEditorFlags: 0
124 | m_IsActive: 1
125 | --- !u!4 &4162741165016952
126 | Transform:
127 | m_ObjectHideFlags: 1
128 | m_CorrespondingSourceObject: {fileID: 0}
129 | m_PrefabInternal: {fileID: 100100000}
130 | m_GameObject: {fileID: 1073555440304004}
131 | m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
132 | m_LocalPosition: {x: 0, y: 0, z: 0}
133 | m_LocalScale: {x: 1, y: 1, z: 1}
134 | m_Children:
135 | - {fileID: 4782709639625668}
136 | - {fileID: 4690729713261028}
137 | - {fileID: 4729002408311522}
138 | - {fileID: 4545879213793466}
139 | - {fileID: 4964883829626994}
140 | m_Father: {fileID: 0}
141 | m_RootOrder: 0
142 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
143 | --- !u!4 &4545879213793466
144 | Transform:
145 | m_ObjectHideFlags: 1
146 | m_CorrespondingSourceObject: {fileID: 0}
147 | m_PrefabInternal: {fileID: 100100000}
148 | m_GameObject: {fileID: 1680671491621748}
149 | m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
150 | m_LocalPosition: {x: 3.334, y: -1.97, z: 0}
151 | m_LocalScale: {x: 0.79621184, y: 0.7962116, z: 0.7962116}
152 | m_Children: []
153 | m_Father: {fileID: 4162741165016952}
154 | m_RootOrder: 3
155 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
156 | --- !u!4 &4690729713261028
157 | Transform:
158 | m_ObjectHideFlags: 1
159 | m_CorrespondingSourceObject: {fileID: 0}
160 | m_PrefabInternal: {fileID: 100100000}
161 | m_GameObject: {fileID: 1948494897459636}
162 | m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
163 | m_LocalPosition: {x: -2.81, y: 2.37, z: 0}
164 | m_LocalScale: {x: 0.79621184, y: 0.7962116, z: 0.7962116}
165 | m_Children: []
166 | m_Father: {fileID: 4162741165016952}
167 | m_RootOrder: 1
168 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
169 | --- !u!4 &4729002408311522
170 | Transform:
171 | m_ObjectHideFlags: 1
172 | m_CorrespondingSourceObject: {fileID: 0}
173 | m_PrefabInternal: {fileID: 100100000}
174 | m_GameObject: {fileID: 1041719462949384}
175 | m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
176 | m_LocalPosition: {x: 3.334, y: 2.37, z: 0}
177 | m_LocalScale: {x: 0.79621184, y: 0.7962116, z: 0.7962116}
178 | m_Children: []
179 | m_Father: {fileID: 4162741165016952}
180 | m_RootOrder: 2
181 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
182 | --- !u!4 &4782709639625668
183 | Transform:
184 | m_ObjectHideFlags: 1
185 | m_CorrespondingSourceObject: {fileID: 0}
186 | m_PrefabInternal: {fileID: 100100000}
187 | m_GameObject: {fileID: 1750859589963992}
188 | m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
189 | m_LocalPosition: {x: 0, y: 0, z: 0}
190 | m_LocalScale: {x: 1.8866842, y: 1.886684, z: 1.886684}
191 | m_Children: []
192 | m_Father: {fileID: 4162741165016952}
193 | m_RootOrder: 0
194 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
195 | --- !u!4 &4964883829626994
196 | Transform:
197 | m_ObjectHideFlags: 1
198 | m_CorrespondingSourceObject: {fileID: 0}
199 | m_PrefabInternal: {fileID: 100100000}
200 | m_GameObject: {fileID: 1821574291237272}
201 | m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
202 | m_LocalPosition: {x: -3.51, y: -1.97, z: 0}
203 | m_LocalScale: {x: 0.79621184, y: 0.7962116, z: 0.7962116}
204 | m_Children: []
205 | m_Father: {fileID: 4162741165016952}
206 | m_RootOrder: 4
207 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
208 | --- !u!23 &23054393140330284
209 | MeshRenderer:
210 | m_ObjectHideFlags: 1
211 | m_CorrespondingSourceObject: {fileID: 0}
212 | m_PrefabInternal: {fileID: 100100000}
213 | m_GameObject: {fileID: 1041719462949384}
214 | m_Enabled: 1
215 | m_CastShadows: 1
216 | m_ReceiveShadows: 1
217 | m_DynamicOccludee: 1
218 | m_MotionVectors: 1
219 | m_LightProbeUsage: 1
220 | m_ReflectionProbeUsage: 1
221 | m_RenderingLayerMask: 4294967295
222 | m_Materials:
223 | - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
224 | m_StaticBatchInfo:
225 | firstSubMesh: 0
226 | subMeshCount: 0
227 | m_StaticBatchRoot: {fileID: 0}
228 | m_ProbeAnchor: {fileID: 0}
229 | m_LightProbeVolumeOverride: {fileID: 0}
230 | m_ScaleInLightmap: 1
231 | m_PreserveUVs: 1
232 | m_IgnoreNormalsForChartDetection: 0
233 | m_ImportantGI: 0
234 | m_StitchLightmapSeams: 0
235 | m_SelectedEditorRenderState: 3
236 | m_MinimumChartSize: 4
237 | m_AutoUVMaxDistance: 0.5
238 | m_AutoUVMaxAngle: 89
239 | m_LightmapParameters: {fileID: 0}
240 | m_SortingLayerID: 0
241 | m_SortingLayer: 0
242 | m_SortingOrder: 0
243 | --- !u!23 &23165215184582192
244 | MeshRenderer:
245 | m_ObjectHideFlags: 1
246 | m_CorrespondingSourceObject: {fileID: 0}
247 | m_PrefabInternal: {fileID: 100100000}
248 | m_GameObject: {fileID: 1750859589963992}
249 | m_Enabled: 1
250 | m_CastShadows: 1
251 | m_ReceiveShadows: 1
252 | m_DynamicOccludee: 1
253 | m_MotionVectors: 1
254 | m_LightProbeUsage: 1
255 | m_ReflectionProbeUsage: 1
256 | m_RenderingLayerMask: 4294967295
257 | m_Materials:
258 | - {fileID: 2100000, guid: c8fac7cd63ac11b47aaa9eeefec804a8, type: 2}
259 | m_StaticBatchInfo:
260 | firstSubMesh: 0
261 | subMeshCount: 0
262 | m_StaticBatchRoot: {fileID: 0}
263 | m_ProbeAnchor: {fileID: 0}
264 | m_LightProbeVolumeOverride: {fileID: 0}
265 | m_ScaleInLightmap: 1
266 | m_PreserveUVs: 1
267 | m_IgnoreNormalsForChartDetection: 0
268 | m_ImportantGI: 0
269 | m_StitchLightmapSeams: 0
270 | m_SelectedEditorRenderState: 3
271 | m_MinimumChartSize: 4
272 | m_AutoUVMaxDistance: 0.5
273 | m_AutoUVMaxAngle: 89
274 | m_LightmapParameters: {fileID: 0}
275 | m_SortingLayerID: 0
276 | m_SortingLayer: 0
277 | m_SortingOrder: 0
278 | --- !u!23 &23317795868008346
279 | MeshRenderer:
280 | m_ObjectHideFlags: 1
281 | m_CorrespondingSourceObject: {fileID: 0}
282 | m_PrefabInternal: {fileID: 100100000}
283 | m_GameObject: {fileID: 1680671491621748}
284 | m_Enabled: 1
285 | m_CastShadows: 1
286 | m_ReceiveShadows: 1
287 | m_DynamicOccludee: 1
288 | m_MotionVectors: 1
289 | m_LightProbeUsage: 1
290 | m_ReflectionProbeUsage: 1
291 | m_RenderingLayerMask: 4294967295
292 | m_Materials:
293 | - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
294 | m_StaticBatchInfo:
295 | firstSubMesh: 0
296 | subMeshCount: 0
297 | m_StaticBatchRoot: {fileID: 0}
298 | m_ProbeAnchor: {fileID: 0}
299 | m_LightProbeVolumeOverride: {fileID: 0}
300 | m_ScaleInLightmap: 1
301 | m_PreserveUVs: 1
302 | m_IgnoreNormalsForChartDetection: 0
303 | m_ImportantGI: 0
304 | m_StitchLightmapSeams: 0
305 | m_SelectedEditorRenderState: 3
306 | m_MinimumChartSize: 4
307 | m_AutoUVMaxDistance: 0.5
308 | m_AutoUVMaxAngle: 89
309 | m_LightmapParameters: {fileID: 0}
310 | m_SortingLayerID: 0
311 | m_SortingLayer: 0
312 | m_SortingOrder: 0
313 | --- !u!23 &23360437394164212
314 | MeshRenderer:
315 | m_ObjectHideFlags: 1
316 | m_CorrespondingSourceObject: {fileID: 0}
317 | m_PrefabInternal: {fileID: 100100000}
318 | m_GameObject: {fileID: 1821574291237272}
319 | m_Enabled: 1
320 | m_CastShadows: 1
321 | m_ReceiveShadows: 1
322 | m_DynamicOccludee: 1
323 | m_MotionVectors: 1
324 | m_LightProbeUsage: 1
325 | m_ReflectionProbeUsage: 1
326 | m_RenderingLayerMask: 4294967295
327 | m_Materials:
328 | - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
329 | m_StaticBatchInfo:
330 | firstSubMesh: 0
331 | subMeshCount: 0
332 | m_StaticBatchRoot: {fileID: 0}
333 | m_ProbeAnchor: {fileID: 0}
334 | m_LightProbeVolumeOverride: {fileID: 0}
335 | m_ScaleInLightmap: 1
336 | m_PreserveUVs: 1
337 | m_IgnoreNormalsForChartDetection: 0
338 | m_ImportantGI: 0
339 | m_StitchLightmapSeams: 0
340 | m_SelectedEditorRenderState: 3
341 | m_MinimumChartSize: 4
342 | m_AutoUVMaxDistance: 0.5
343 | m_AutoUVMaxAngle: 89
344 | m_LightmapParameters: {fileID: 0}
345 | m_SortingLayerID: 0
346 | m_SortingLayer: 0
347 | m_SortingOrder: 0
348 | --- !u!23 &23512684803758586
349 | MeshRenderer:
350 | m_ObjectHideFlags: 1
351 | m_CorrespondingSourceObject: {fileID: 0}
352 | m_PrefabInternal: {fileID: 100100000}
353 | m_GameObject: {fileID: 1948494897459636}
354 | m_Enabled: 1
355 | m_CastShadows: 1
356 | m_ReceiveShadows: 1
357 | m_DynamicOccludee: 1
358 | m_MotionVectors: 1
359 | m_LightProbeUsage: 1
360 | m_ReflectionProbeUsage: 1
361 | m_RenderingLayerMask: 4294967295
362 | m_Materials:
363 | - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
364 | m_StaticBatchInfo:
365 | firstSubMesh: 0
366 | subMeshCount: 0
367 | m_StaticBatchRoot: {fileID: 0}
368 | m_ProbeAnchor: {fileID: 0}
369 | m_LightProbeVolumeOverride: {fileID: 0}
370 | m_ScaleInLightmap: 1
371 | m_PreserveUVs: 1
372 | m_IgnoreNormalsForChartDetection: 0
373 | m_ImportantGI: 0
374 | m_StitchLightmapSeams: 0
375 | m_SelectedEditorRenderState: 3
376 | m_MinimumChartSize: 4
377 | m_AutoUVMaxDistance: 0.5
378 | m_AutoUVMaxAngle: 89
379 | m_LightmapParameters: {fileID: 0}
380 | m_SortingLayerID: 0
381 | m_SortingLayer: 0
382 | m_SortingOrder: 0
383 | --- !u!33 &33051478306752160
384 | MeshFilter:
385 | m_ObjectHideFlags: 1
386 | m_CorrespondingSourceObject: {fileID: 0}
387 | m_PrefabInternal: {fileID: 100100000}
388 | m_GameObject: {fileID: 1750859589963992}
389 | m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
390 | --- !u!33 &33564632248098672
391 | MeshFilter:
392 | m_ObjectHideFlags: 1
393 | m_CorrespondingSourceObject: {fileID: 0}
394 | m_PrefabInternal: {fileID: 100100000}
395 | m_GameObject: {fileID: 1041719462949384}
396 | m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
397 | --- !u!33 &33710475515543736
398 | MeshFilter:
399 | m_ObjectHideFlags: 1
400 | m_CorrespondingSourceObject: {fileID: 0}
401 | m_PrefabInternal: {fileID: 100100000}
402 | m_GameObject: {fileID: 1948494897459636}
403 | m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
404 | --- !u!33 &33934235896704876
405 | MeshFilter:
406 | m_ObjectHideFlags: 1
407 | m_CorrespondingSourceObject: {fileID: 0}
408 | m_PrefabInternal: {fileID: 100100000}
409 | m_GameObject: {fileID: 1821574291237272}
410 | m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
411 | --- !u!33 &33966280038937740
412 | MeshFilter:
413 | m_ObjectHideFlags: 1
414 | m_CorrespondingSourceObject: {fileID: 0}
415 | m_PrefabInternal: {fileID: 100100000}
416 | m_GameObject: {fileID: 1680671491621748}
417 | m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
418 | --- !u!65 &65047797416528598
419 | BoxCollider:
420 | m_ObjectHideFlags: 1
421 | m_CorrespondingSourceObject: {fileID: 0}
422 | m_PrefabInternal: {fileID: 100100000}
423 | m_GameObject: {fileID: 1041719462949384}
424 | m_Material: {fileID: 0}
425 | m_IsTrigger: 0
426 | m_Enabled: 1
427 | serializedVersion: 2
428 | m_Size: {x: 1, y: 1, z: 1}
429 | m_Center: {x: 0, y: 0, z: 0}
430 | --- !u!65 &65227327944083028
431 | BoxCollider:
432 | m_ObjectHideFlags: 1
433 | m_CorrespondingSourceObject: {fileID: 0}
434 | m_PrefabInternal: {fileID: 100100000}
435 | m_GameObject: {fileID: 1750859589963992}
436 | m_Material: {fileID: 0}
437 | m_IsTrigger: 0
438 | m_Enabled: 1
439 | serializedVersion: 2
440 | m_Size: {x: 1, y: 1, z: 1}
441 | m_Center: {x: 0, y: 0, z: 0}
442 | --- !u!65 &65262898154187970
443 | BoxCollider:
444 | m_ObjectHideFlags: 1
445 | m_CorrespondingSourceObject: {fileID: 0}
446 | m_PrefabInternal: {fileID: 100100000}
447 | m_GameObject: {fileID: 1821574291237272}
448 | m_Material: {fileID: 0}
449 | m_IsTrigger: 0
450 | m_Enabled: 1
451 | serializedVersion: 2
452 | m_Size: {x: 1, y: 1, z: 1}
453 | m_Center: {x: 0, y: 0, z: 0}
454 | --- !u!65 &65423381411825540
455 | BoxCollider:
456 | m_ObjectHideFlags: 1
457 | m_CorrespondingSourceObject: {fileID: 0}
458 | m_PrefabInternal: {fileID: 100100000}
459 | m_GameObject: {fileID: 1680671491621748}
460 | m_Material: {fileID: 0}
461 | m_IsTrigger: 0
462 | m_Enabled: 1
463 | serializedVersion: 2
464 | m_Size: {x: 1, y: 1, z: 1}
465 | m_Center: {x: 0, y: 0, z: 0}
466 | --- !u!65 &65510122804828914
467 | BoxCollider:
468 | m_ObjectHideFlags: 1
469 | m_CorrespondingSourceObject: {fileID: 0}
470 | m_PrefabInternal: {fileID: 100100000}
471 | m_GameObject: {fileID: 1948494897459636}
472 | m_Material: {fileID: 0}
473 | m_IsTrigger: 0
474 | m_Enabled: 1
475 | serializedVersion: 2
476 | m_Size: {x: 1, y: 1, z: 1}
477 | m_Center: {x: 0, y: 0, z: 0}
478 | --- !u!114 &114111543808520302
479 | MonoBehaviour:
480 | m_ObjectHideFlags: 1
481 | m_CorrespondingSourceObject: {fileID: 0}
482 | m_PrefabInternal: {fileID: 100100000}
483 | m_GameObject: {fileID: 1821574291237272}
484 | m_Enabled: 1
485 | m_EditorHideFlags: 0
486 | m_Script: {fileID: 11500000, guid: 835ec75000b58cf4aba592fb846a629b, type: 3}
487 | m_Name:
488 | m_EditorClassIdentifier:
489 | rotation: {x: 0, y: 0, z: 0}
490 | --- !u!114 &114115697624970392
491 | MonoBehaviour:
492 | m_ObjectHideFlags: 1
493 | m_CorrespondingSourceObject: {fileID: 0}
494 | m_PrefabInternal: {fileID: 100100000}
495 | m_GameObject: {fileID: 1041719462949384}
496 | m_Enabled: 1
497 | m_EditorHideFlags: 0
498 | m_Script: {fileID: 11500000, guid: 835ec75000b58cf4aba592fb846a629b, type: 3}
499 | m_Name:
500 | m_EditorClassIdentifier:
501 | rotation: {x: 0, y: 0, z: 0}
502 | --- !u!114 &114194695389752790
503 | MonoBehaviour:
504 | m_ObjectHideFlags: 1
505 | m_CorrespondingSourceObject: {fileID: 0}
506 | m_PrefabInternal: {fileID: 100100000}
507 | m_GameObject: {fileID: 1750859589963992}
508 | m_Enabled: 1
509 | m_EditorHideFlags: 0
510 | m_Script: {fileID: 11500000, guid: 835ec75000b58cf4aba592fb846a629b, type: 3}
511 | m_Name:
512 | m_EditorClassIdentifier:
513 | rotation: {x: 0, y: 0, z: 0}
514 | --- !u!114 &114491102716508916
515 | MonoBehaviour:
516 | m_ObjectHideFlags: 1
517 | m_CorrespondingSourceObject: {fileID: 0}
518 | m_PrefabInternal: {fileID: 100100000}
519 | m_GameObject: {fileID: 1680671491621748}
520 | m_Enabled: 1
521 | m_EditorHideFlags: 0
522 | m_Script: {fileID: 11500000, guid: 835ec75000b58cf4aba592fb846a629b, type: 3}
523 | m_Name:
524 | m_EditorClassIdentifier:
525 | rotation: {x: 0, y: 0, z: 0}
526 | --- !u!114 &114733796681027892
527 | MonoBehaviour:
528 | m_ObjectHideFlags: 1
529 | m_CorrespondingSourceObject: {fileID: 0}
530 | m_PrefabInternal: {fileID: 100100000}
531 | m_GameObject: {fileID: 1948494897459636}
532 | m_Enabled: 1
533 | m_EditorHideFlags: 0
534 | m_Script: {fileID: 11500000, guid: 835ec75000b58cf4aba592fb846a629b, type: 3}
535 | m_Name:
536 | m_EditorClassIdentifier:
537 | rotation: {x: 0, y: 0, z: 0}
538 | --- !u!114 &114780491621147760
539 | MonoBehaviour:
540 | m_ObjectHideFlags: 1
541 | m_CorrespondingSourceObject: {fileID: 0}
542 | m_PrefabInternal: {fileID: 100100000}
543 | m_GameObject: {fileID: 1750859589963992}
544 | m_Enabled: 1
545 | m_EditorHideFlags: 0
546 | m_Script: {fileID: 11500000, guid: a6886d548845d24468b7160b582ef670, type: 3}
547 | m_Name:
548 | m_EditorClassIdentifier:
549 |
--------------------------------------------------------------------------------
/Samples~/Rich Presence/Spinning Cubes.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ea9c4ccd537827b4582636e312bb0471
3 | NativeFormatImporter:
4 | externalObjects: {}
5 | mainObjectFileID: 100100000
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.lachee.discordrpc",
3 | "version": "1.1.1",
4 | "displayName": "Discord Rich Presence",
5 | "description": "Implements the RPC Protocol for discord using Discord RPC CSharp",
6 | "unity": "2018.4",
7 | "documentationUrl": "https://lachee.github.io/discord-rpc-unity/",
8 | "changelogUrl": "https://github.com/Lachee/discord-rpc-unity/commits/master",
9 | "license": "MIT",
10 | "dependencies": {
11 | "com.unity.nuget.newtonsoft-json": "3.0.3"
12 | },
13 | "samples": [
14 | {
15 | "displayName": "Basic Usage",
16 | "description": "How to use the basic functionality of the discord manager",
17 | "path": "Samples~/Rich Presence"
18 | }
19 | ],
20 | "keywords": [
21 | "Discord",
22 | "Rich Presence",
23 | "discord-rpc-csharp",
24 | "RPC"
25 | ],
26 | "author": {
27 | "name": "Lachee",
28 | "url": "https://lachee.dev"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/package.json.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 202c780ef5910ff41adf6199499b6aac
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------