Photonプラグイン よくある質問
設定
Photon Serverのカスタムプラグインの設定方法は?
app.configにPlugin XMLノードを追加する必要があります。必要最低限の要素は以下のとおりです:
XML
<PluginSettings Enabled="true">
<Plugins>
<Plugin
AssemblyName="{filename}.dll"
Version=""
Type="{namespace}.PluginFactory" />
</Plugins>
</PluginSettings>
Webhookプラグインの設定例:
XML
<PluginSettings Enabled="true">
<Plugins>
<Plugin
Name="WebHooks"
Version=""
AssemblyName="PhotonHive.WebhooksPlugin.dll"
Type="Photon.Hive.Plugin.WebHooks.PluginFactory"
BaseUrl="<custom webhooks base url>"
IsPersistent="true"
HasErrorInfo="true"
PathClose="GameClose"
PathCreate="GameCreate"
PathEvent="GameEvent"
PathGameProperties="GameProperties"
PathJoin="GameJoin"
PathLeave="GameLeave"
PathLoad="GameCreate" />
</Plugins>
</PluginSettings>
詳細は、プラグインマニュアルの「設定」セクションを参照してください。
Photonは複数のプラグインをサポートしていますか?
アプリケーションには1度に1つのプラグインアセンブリ(DLL)やプラグインファクトリしか設定することができません。 このDLLでは、いくつでもプラグインを持つことができます。 ルーム作成時に読み込まれ、インスタンス化されるプラグインは1つです。 プラグインのルームとインスタンスの間には1対1の関係があります。つまり、各ルームにはプラグインの独自のインスタンスがあります。
ルーム作成時に、どのプラグインを使用するか選択する方法は?
私たちのプラグインモデルは、ファクトリのパターンを使用しています。
プラグインは、必要に応じて名前からインスタンス化されます。
クライアントはroomOptions.Plugins
を使用して、プラグインのセットアップをリクエストしルームを作成します。 roomOptions.Plugins
はstring[]
型で、最初の文字列(roomOptions.Plugins[0]
)はファクトリに渡されるプラグイン名にする必要があります。
例:
roomOptions.Plugins = new string[] { "NameOfYourPlugin" };
または
roomOptions.Plugins = new string[] { "NameOfOtherPlugin" };
クライアントが何も送信しない場合、サーバーはデフォルト(何も設定されていない場合)、または設定されている場合はプラグインファクトリが作成時に返すものを使用します。
ファクトリでは以下のように名前を使用して、対応するプラグインを読み込みます:
C#
public class PluginFactory : IPluginFactory
{
public IGamePlugin Create(IPluginHost gameHost, string pluginName, Dictionary<string, string> config, out string errorMsg)
{
var plugin = new DefaultPlugin(); // default
switch(pluginName){
case "Default":
// name not allowed, throw error
break;
case "NameOfYourPlugin":
plugin = new NameOfYourPlugin();
break;
case "NameOfOtherPlugin":
plugin = new NameOfOtherPlugin();
break;
default:
//plugin = new DefaultPlugin();
break;
}
if (plugin.SetupInstance(gameHost, config, out errorMsg))
{
return plugin;
}
return null;
}
}
PluginFactory.Create
で返されたプラグインの名前がクライアントによってリクエストされたものと一致しない場合は、 プラグインはアンロードされ、クライアントの作成または参加オペレーションは失敗してPluginMismatch (32757)
エラーとなります。
実行時にディスクからファイルを読み込みたいです。プラグインは、ファイルシステムへのアクセス権を持っていますか?外部のサーバーからファイルをダウンロードする必要がありますか?
プラグインに必要不可欠なものと一緒に、追加のファイルをアップロードすることができます。 ファイルへのパスは、その後typeof(yourplugin).Assembly.Location
を使用して取得することができます。
依存モジュール(DLL)は自動的にメインプラグインDLLと共に読み込まれますか?なぜSystem.TypeLoadException
を取得してしまうのでしょう?
外部モジュールの使用は、プラグインソリューションのDLLやプロジェクトを参照することで動作します。 参照されるすべての依存関係のモジュールをプラグインDLLと同じディレクトリにデプロイし、それらが読み込まれてリンクされることを確認してください。
コールバック
プレイヤーがルームに入ろうとすると、どのメソッドが呼ばれますか?
この質問に答えるには、作成、参加、再参加によりプレイヤーがルームに入る際のシナリオが4つある点を理解しなければなりません。
一般的にJoinRoom
とCreateRoom
オペレーションの構造は非常に似ています。 異なるJoinMode
の値を持つ1つの論理的なJoinオペレーションと考えると良いかもしれません。
- Create:
OpCreateRoom
:ルームがすでに存在している場合、エラーが返されます。 - Join:
OpJoinRoom
(JoinMode.Default
または設定されていない)、OpJoinRandomRoom
: ルームが存在しない場合、エラーが返されます。 - CreateIfNotExists:
OpJoinRoom
(JoinMode.CreateIfNotExist
): ルームが存在しない場合、ルームを作成します。 - RejoinOnly:
OpJoinRoom
(JoinMode.RejoinOnly
): アクターがルームにすでに存在しない場合、エラーが発生します。
ルームがメモリに有る場合、2)から4)はBeforeJoin
をトリガーし、{ICallInfo}.Continue()
を呼ぶことを仮定して、OnJoin
が呼び出されます。
プラグインが設定されアクターがルームに追加される直前、ルーム作成の直後にOnCreateGame
が呼び出されます。
これは1)、3)および4)によってトリガーすることができます。
後者は、状態の保存と読み込みが正常に処理された場合にのみ発生します。 これは、プレイヤーがサーバーのメモリから削除されたルームへの再参加をリクエストしたときに発生します。
Photonは、それでも作成を行いプラグインを設定します。
プラグインは、データベースまたは外部サービスからシリアル化された状態を取得し、SetSerializedGameState
を呼びます。 状態には、インアクティブ状態のアクターリストが含まれます。再入室によって、アクターは再び有効になります。
プラグインコールバックでアクター番号を取得する方法は?
プラグインhookでActorNrを取得する方法は以下のとおりです:
1. OnCreateGame, info.IsJoin == false:
ゲームの最初のアクターで、ゲームを作成したアクターです。アクター番号は常に1に設定されます。
2. OnCreateGame, info.IsJoin == true:
a. info.Continue()の前;
ゲーム状態の読み込みによって、ルームが「再作成」される場合に備えて、インアクティブユーザーのリストから、UserIdでActorNrを取得してください。
読み込んだルームの状態からUserIdと比較してActorListを確認し、ゲーム状態でアクター番号を検索する必要があります。 (UserIdが利用できない場合には再参加は失敗し、CheckUserOnJoinとアクターごとのUserIdによってルームを作成しなければなりません)
C#
// load State from webservice or database or re-construct it
if (this.PluginHost.SetGameState(state))
{
int actorNr = 0;
foreach (var actor in PluginHost.GameActorsInactive)
{
if (actor.UserId == info.UserId)
{
actorNr = actor.ActorNr;
break;
}
}
if (actorNr == 0)
{
if (!asyncJoin)
{
// error, join will fail with
// ErrorCode.JoinFailedWithRejoinerNotFound = 32748, // 0x7FFF - 19,
}
else
{
actorNr = PluginHost.GetSerializableGameState().ActorCounter + 1;
}
}
}
その他の場合、「正しい」ActorNrをJoinRoomオペレーションでクライアントから送信すると、info.Request.ActorNr
から取得できます。
b. info.Continue()の後;
アクティブなアクターのリストからUserIdでctorNrを取得します。
C#
int actorNr;
foreach (var actor in PluginHost.GameActorsActive)
{
if (actor.UserId == info.UserId)
{
actorNr = actor.ActorNr;
break;
}
}
3. BeforeJoin
a. info.Continue()の前;
C#
int actorNr = 0;
switch (info.Request.JoinMode)
{
case JoinModeConstants.JoinOnly:
case JoinModeConstants.CreateIfNotExists:
actorNr = PluginHost.GetSerializableGameState().ActorCounter + 1;
break;
case JoinModeConstants.RejoinOnly:
foreach (var actor in PluginHost.GameActorsInactive)
{
if (actor.UserId == info.UserId)
{
actorNr = actor.ActorNr;
break;
}
}
if (actorNr == 0)
{
// error, join will fail with
// ErrorCode.JoinFailedWithRejoinerNotFound = 32748, // 0x7FFF - 19,
}
break;
case JoinModeConstants.RejoinOrJoin:
foreach (var actor in PluginHost.GameActorsInactive)
{
if (actor.UserId == info.UserId)
{
actorNr = actor.ActorNr;
break;
}
}
if (actorNr == 0)
{
actorNr = PluginHost.GetSerializableGameState().ActorCounter + 1;
}
break;
}
その他の場合、「正しい」ActorNrをJoinRoomオペレーションでクライアントから送信すると、info.Request.ActorNr
から取得できます。
b. info.Continue()の後;
アクティブなアクターのリストから、UserIdでActorNrを取得します。
C#
int actorNr;
foreach (var actor in this.PluginHost.GameActorsActive)
{
if (actor.UserId == info.UserId)
{
actorNr = actor.ActorNr;
break;
}
}
4. OnJoin, OnRaiseEvent, BeforeSetProperties, OnSetProperties, OnLeave:
利用可能なinfo.ActorNr
を使用してください。
5. BeforeCloseGame, OnCloseGame:
hookがクライアントオペレーションではなくサーバーによってトリガーされているため、ActorNrを取得する方法はありません。
サーバーからルームを作成できますか?
いいえ、それは不可能です。
サーバーからルームを削除できますか?
いいえ、それは不可能です。
プラグインイベントで利用可能なデータを把握する、最適な方法は?
一般的に、イベントのICallInfo
コールバックパラメータは必要なものを公開する必要があります。
多くの場合、実際のオペレーションは{ICallInfo}.OperationRequest
(またはRequest
)プロパティによって取得可能なパラメータをリクエストします。
UseStrictModeは何を行いますか?
プラグインの概念は、受信リクエストを処理する前または後に、「通常」のPhotonフローにフックすることです。
当初、弊社はユーザーが何かを呼び出すことを強制しませんでした。これは、本質的に受信したリクエストのデフォルト処理をキャンセルすることと同じでした。
これにより、ディベロッパーはデフォルトのコールバックロジックを使用せずに、必要なものを一から実装しなければなりません。結果的に複数の予期せぬ問題が発生しました。ディベロッパーは常にキャンセルをしたいわけではありません。
そこでstrictモードを導入し、ディベロッパーが決定できるようにしました。
現在はContinue
、Fail
または Cancel
のいずれかを1回のみ呼びます。また、処理を遅延させるDefer
メソッドもあります。
PluginBase
のコールバックメソッドをオーバーライドするときbase.XXX()
を呼ぶ必要はありますか?
PluginBase
内のすべてのコールバックメソッドには、最後に行われるContinue()
への呼び出しが含まれています。
概念としては、PluginBase
から継承することで、自分が興味のあるメソッドのみオーバーライドすることができます。
base.XXX()
の前か後にコードの追加が必要な場合や、 デフォルトの動作の完全な変更が必要な場合があります。
前者の場合は、ICallInfo
処理メソッドに呼び出しを追加すべきではありません。
後者の場合は、base.XXX()
を呼び出さず、一からメソッドの独自の実装を行い、利用可能なICallInfo処理メソッドの1つに呼び出しを追加します。
唯一の例外は、PluginBase.OnLeave
です。 このメソッドは、現在のMasterClientが退出する場合にMasterClientの変更を処理します。
イベント
HttpForwardプロパティとWebFlagsクラスは、何を行ないますか?
これらはWebHookに関連する機能です。
WebFlags
はWebHook v1.2のプラグインで導入されています。WebFlagの詳細についてはこのページを参照してください。 HttpForward
は、対応するwebflagの値を示すプロパティです。
本来はWebHookとWebRPCのために作られていますが、プラグインでそれらを使用することも可能です。
プラグインからイベントを送信する方法は?
これにはPluginHost.BroadcastEvent
を使用する必要があります。 クライアントからイベントを送信するのと同じ様に動作しますが、サーバーを表すために送信元アクター番号を0に設定することができるのが主な違いです。
C#
this.PluginHost.BroadcastEvent(
recieverActors: new int[] { targetPlayerNr },
senderActor: 0,
data: new Dictionary<byte, object>() { { 1, data } },
evCode: (byte)code,
cacheOp: 0,
sendParameters: new SendParameters() { Unreliable = false });
詳細は「プラグインからのイベントの送信」セクションを参照してください。
OnRaiseEvent
から行なった場合とは異なりOnJoin
コールバック内で呼ばれた場合にPluginHost.BroadcastEvent
がクライアントへのイベントの送信に成功しないのはなぜですか?
OnRaiseEvent
ではクライアントが既に参加しているので、イベントを受信できます。 OnJoin
では、{ICallInfo}.Continue()
を使用してリクエストが処理されない限り、クライアントは完全に参加されません。
つまり、{ICallInfo}.Continue()
の後にPluginHost.BroadcastEvent
が呼ばれた場合、イベントはターゲットクライアントによって受信される必要があります。
プラグインhookは、以下のように動作します:{ICallInfo}.Continue()
への呼び出しで、Photonの通常の処理をトリガーします。
この場合、完全に参加した後にイベントを送信したほうが理にかなっています。
プラグインから送信されたイベントデータをクライアントが受信できないのはなぜですか?
これは既知の問題です。クライアントは、イベントがあらかじめ定義された特定の構造と既知のキーコードを有することを想定しています。 イベントデータは245、アクター番号は254です。 これを修正するには、プラグインからイベントデータを送信する方法で軽微な変更を行います。(Dictionary<byte,object>)eventData
の代わりにnew Dictionary<byte,object>(){{245,eventData},{254,senderActorNr}}
を送信します。
プラグインはカスタムオペレーションに対応していますか?
いいえ、現状プラグインはカスタムオペレーションをサポートしていません。
Photonプラグインは、一連のネイティブオペレーション(「Create」、「Join」、「SetProperties」、「RaiseEvent」および 「Leave」)のみに対応するコールバックを提供します。 カスタムオペレーション(セルフホスティングされたPhoton Serverで追加済み)を受信することはできません。 また、プラグインSDKを使用して新しいものを拡張することはできません。
しかし、2方向のイベントをやりとりすることで、同じ結果を得ることができます。
- クライアントからプラグインにLoadBalancinglient.OpRaiseEventを呼び出す。
- プラグインからクライアントにPluginHost.BroadcastEventを呼び出す。
ゲームの状態
「アクティブ」ユーザーと「インアクティブ」ユーザーはどのように区別されますか?
プレイヤーが切断すると、Photonは通常クリーンアップを行いアクターが削除されますが、 ルームを作成するときにクライアント上で CreateOptions
に PlayerTTL
を定義できます。
正の数である場合、Photonはクリーンアップを開始する前にその時間(ミリ秒単位で定義)待ちます。
その間、アクターはインアクティブとみなされ、ゲームに再度参加することができます。それが正常に行われた場合、プレイヤーは再びアクティブになります。
接続の不良によりプレイヤーが切断されても短い時間内(数分)であれば戻ることのできるようなRTSゲームなどの場合に、ゲームの状態を保存してプレイヤーを継続させるために、この機能を使用することができます。
PluginHost.GameActorsActive
には、ルーム内(参加済み)のすべてのアクターが含まれていて、PluginHost.GameActorsInActive
にはルームを退出した(放棄せずに)すべてのアクターが含まれています。
プラグインで、アクターをルームから退出させることはできますか?その方法は?
はい、可能です。プラグインクラスからは、PluginHost.RemoveActor(int actorNr, string reasonDetails)
を呼び出す必要があります。3つのパラメータ:PluginHost.RemoveActor(int actorNr, byte reason, string reasonDetails)
を受け取るメソッド負荷を呼び出すことで、理由を設定できます。
ルームの状態を保持するには?
ルームの状態を保存するには:
- PluginHost.GetGameStateを呼び、SerializableGamestateを取得します。
- 状態をシリアル化します(たとえば、JSON)。
- データストアに状態を保存します。
ルームの状態を読み込むには:
- データストアから状態を取得します
- 状態を非シリアル化します。
PluginHost.SetGameState
を呼びます。
注: {ICallInfo}.Continue()
を呼ぶ前には、OnCreateGame
でPluginHost.SetGameState
を呼ぶことのみが許可されています。
SerializableGameState
内のいくつかのカスタムルームプロパティを見つけることができません。ロビーのプロパティのみなのはなぜですか?
シリアル化が可能なゲーム状態では、設計によりすべてのカスタムプロパティへのアクセスが提供されるわけではありません。
ロビーで共有したもののみが公開され、それは「閲覧」目的のみです。
組み合わせたすべてのプロパティは、バイナリ配列に含まれています。
これによりJSONにシリアライズ化し、デシリアライズ化で戻した時に型情報を失わずにすみます。
この機能は、主に保存/読み込みのシナリオ向けに設計されています。
将来的にこの挙動は変更される可能性があります。
プラグインからルームプロパティ(MaxPlayers
, IsVisible
, IsOpen
など)にアクセスする方法は?
すべてのルームプロパティは、Hashtable
型であるPluginBase.PluginHost.GameProperties
からアクセス可能です。
これらのプロパティには、「ネイティブ」または「既知」のものが含まれます。
それらには、LoadBalancingクライアントSDK、またはそれぞれのAPIリファレンスページによって、LoadBalancing.GamePropertyKey
から取得可能な値を参照するbyte
キーがあります。
int
(例:MaxPlayers = (byte)255
)からbyte
にキーをキャストしてください。
将来的には、これらの値を取得するためのより良い方法を提供する予定です。
カスタムのルームプロパティはPluginHost.GameProperties
にも含まれます。
ディベロッパーは、責任を持ってこれらのキー/値の処理をおこなってください。
一方、ロビーから見えるカスタムプロパティのみが、Dictionary<string, object>
である PluginBase.PluginHost.CustomGameProperties
に格納されます。
以下のように、プラグインからルームとアクターのプロパティにアクセス(読み込みおよび書き込み)することができます:
読み込みの例:
C#
PluginHost.GameProperties.ContainsKey("map");
PluginHost.GameActors[1].Properties["health"];
書き込みの例:
C#
PluginHost.SetProperties(actorNr: 0, properties: new Hashtable() { { "map", "america" } }, expected: null, broadcast: false); // actor=0 for Room properties
PluginHost.SetProperties(actorNr: 1, properties: new Hashtable() { { "health", 100 } }, expected: null, broadcast: true);
スレッディング
.NET Pluginコンポーネントでの、スレッディングの要件について詳細を記載します。
単独のPhoton Serverでいくつのスレッドが実行されていますか?
スレッドの利用は以下のように分けられます:
- ネイティブ - 9個のスレッド
- 管理 - .NETデフォルト設定を使用する.NET ThreadPoolにもとづく。
この設定には非常に厳密にテストをおこなってきましたので、広範な負荷プロファイルに対して問題なく動作します。
管理スレッドの使用は、状況によって異なります(.NET Windows Performanceカウンターに記載されたとおりです):
a) 1個~12個まで:通常のPhoton Cloud Realtime負荷に対応
b) 35個以上:プラグイン間の通信(ロッキング)をともなうプラグインを実行するカスタマークラウドが例で、より高いコンテンションの原因となります(弊社のコードとは対照的です)。
備考:必要に応じて.NET ThreadPool設定は調整できます。
これまでデフォルトの設定で適切な成果が得られていますが、各バージョンに応じて調整が必要な可能性があります。
Photonのホストはフリースレッドですか?すべてのスレッドがいつでもすべてのルームにアクセスできますか?
メッセージパッシングアーキテクチャを使用しています:プラグインは一度に1つのスレッドにのみ呼び出されます。 しかし、スレッドプールを使用するので各呼び出しのスレッドは同じではないかもしれません。
プラグインの書き込み時に、スレッドでの安全性の問題点はありますか?
一般的に、プラグインへのすべてのコールがシリアル化されていると想定するのが安全です(実質的に1つのスレッド上/物理的に同じスレッド上である必要はありません)。
Enterprise Cloud
Enterprise Cloudのランタイム環境に関連した質問を以下に記載します。
プラグインの設定方法は?
Photon Enterprise Cloudには:
Photonダッシュボードからアプリケーションの管理ページに移動して、新しいプラグインを追加する必要があります。
そこから、ページ下部の「Create a new Plugin」ボタンをクリックしてください。
これで、キー/値のエントリを追加することでプラグインを設定することができます。
AssemblyName
、Version
、Path
、およびType
は必須です。
Photonプラグインを作成する際のパイプライン処理は何ですか?
Photonプラグイン作成時のパイプライン処理は簡単です:
- 必要なSDKとサーバーバイナリをダウンロードします。
- プラグインアセンブリをコーディングおよび構築します。
- デプロイし、テストします。
- アップロードします。
- 設定します。
Photonプラグインの環境設定は以下のとおりです:
- 開発: ローカルマシン
- テスト: ローカルネットワーク
- ステージング: クラウドの個別のAppID
- 本番: クラウドの本番のAppID
プラグインのアップロードは自動化されていますか?
Enterprise Cloudをご利用中のお客様にはPowerShellスクリプトが提供され、Privateクラウドの管理に役立ちます。 詳細はプラグインアップロードオンラインガイド(~~~/plugins/plugins-upload-guide)で確認してください。
プラグインのパフォーマンスを監視するには?
ダッシュボードで利用できる複数のカウンターは、トラッキングされています。
また、カスタムのカウンターを追加したり、カウンター用に外部ツールを統合することも可能です(例:New Relic)。
これらのサービスを使用する場合には、弊社とのコンサルティング契約が必要となります。
####プラグインにログを設定する方法はありますか?
当社のサーバー上のログファイルへのアクセスは許可されていません。
このため、ログや警告には外部サービスを使用する必要があります。
Logentries または Papertrailの使用をお勧めします。
Enterprise Cloudをご利用中のお客様は、弊社にご連絡ください。お使いのプライベートクラウドに、ご希望のロギングサービスを設定いたします。
Logentriesをご希望の場合には 設定済みのログトークンを連絡してください。
Papertrailをご希望の場合には、カスタムURLとポートを連絡してください。
ログエントリートークンを取得する方法は?
Logentriesアカウントを作成し、以下の3つの手順にしたがってください:
- 「Logs"/"Add New Log」を選択します。
- 「Libraries"/".NET」を選択します。
- ログセットに名前を入力します。
- 「Create Log Token」をクリックします。
- 「Finish & View Log」をクリックします。
- 新しいログセットを選び、「Setting」タブを選択します。これでトークンを参照できるようになります。
- メールでトークンを送信してください。
Papertrail URLを取得する方法は?
Papertrailアカウントを作成し、以下の手順にしたがってください:
- Add "System" 「System」を追加します。
- 次のページの上部に「Your logs will go to logs6.papertrailapp.com:12345 and appear in Events.」といった文言が表示されます。
- メールでそのURLを送信してください。
新たなプラグインバージョンをリリースするために推奨される方法とは?
現在、Photonのプラグインはサイド・バイ・サイドのアセンブリバージョニングのみをサポートしています:AppIDごとに1つのプラグインDLLバージョンです。
新しいプラグインバージョンを展開するには、以下の2つの方法を推奨します:
A. 「互換性のある」プラグインの展開:新たなクライアントバージョンは不要です。
- 新しいバージョンのプラグインアセンブリをアップロードします。
- AppIDをステージングする際:新たなバージョンが予期されたとおりに動作している点を確認してください(推奨)。
- 新たなプラグインアセンブリバージョンを使用するため、本番のAppID設定を更新します。
B. 「互換性のない」プラグインの展開:新たなクライアントバージョンが必要です。
- プラグインアセンブリの新たなバージョンをアップロードします。
- 新たな本番のAppIDを設定します。
- 新たなプラグインアセンブリバージョンを使用するため、新たな本番のAppIDを設定します。
さらに高度なテクニックは以下のとおりです:
実際のゲームロジックは明示的に読み込まれる別のDLLからですが、コアサーバーアップデートループをもつプラグインDLLを構築することも可能です。
バージョンごとにゲームロジックは1つ以上のDLLを持つ必要がありますが、コアプラグインDLLは更新されるべきではありません。
コアプラグインDLLは、クライアントバージョンにもとづき適切なゲームロジックDLLを読み込みます。
これは、完全な互換性のためにサーバーサイドコードをクライアントサイドコードにマッピングするのと同様です。
これによって、プラグインの互換性のあるバージョンアップデートが可能になります:
このため、新たなプラグインバージョンがリリースされた際にクライアントにアップデートを強いる必要がありません。
ゲームロジックDLLをバージョンごとに個別のフォルダ、同じフォルダ内でバージョン内で異なる名前で格納することができます。
プラグイン内に静的なフィールドを使用できますか?
ルーム間やアプリケーション間で、同じプラグインが共有されます。
同じプラグインクラスの静的フィールドも、同様に共有されます。
静的フィールドの使用を回避できない場合、2つのアプリケーションで同じプラグインアセンブリの使用を回避するための方法は以下のとおりです:
異なる2つのプラグイン名の下に、同じプラグインファイルをアップロードします:
a- 名前Xでプラグインアーカイブをアップロード
b- 名前Yでプラグインアーカイブをアップロード2つのアプリケーションに、「Path」以外は同じ設定を適用:
a- プラグインXを使用するようアプリAを設定:"{customerName}\X"
b- プラグインYを使用するようアプリBを設定:"{customerName}\X"
その他
プラグインがRemoveActorを実行した際に、特定の理由を取得する方法は?
現在、弊社はクライアントに int
または string
型の理由を送信しません。
クライアントを切断する前に、カスタムイベントを使用してクライアントに通知をおこなうことができます。
たとえば、理由とともにカスタムイベントを送信し、タイマーを使用してRemoveActor
200ms laterをスケジュールすることも可能です。
これらのヘルパーメソッドを利用することができます:
C#
private const int RemoveActorEventCode = 199;
private const int RemoveActorTimerDelay = 200;
private void RemoveActor(int actorNr, string reason)
{
this.PluginHost.BroadcastEvent(new List<int> { actorNr }, 0, RemoveActorEventCode,
new Dictionary<byte, object> { { 254, 0 }, { 245, reason }}, 0);
this.PluginHost.CreateOneTimeTimer(() => this.PluginHost.RemoveActor(actorNr, reason),
RemoveActorTimerDelay);
}
private void RemoveActor(int actorNr, byte reasonCode, string reason)
{
this.PluginHost.BroadcastEvent(new List<int> { actorNr }, 0, RemoveActorEventCode,
new Dictionary<byte, object> { { 254, 0 }, { 245, new { reasonCode, reason } }}, 0);
this.PluginHost.CreateOneTimeTimer(() => this.PluginHost.RemoveActor(actorNr, reason),
RemoveActorTimerDelay);
}
クライアントSDKはカスタム型を登録することにより、シリアル化の拡張に対応します。サーバー上でこれらの型を非シリアル化する方法は?
クライアントSDKと同様に、カスタム型は以下のように登録できます:
C#
PluginHost.TryRegisterType(type: typeof (CustomPluginType), typeCode: 1, serializeFunction: SerializeFunction, deserializeFunction:
DeserializeFunction);
詳細は プラグインマニュアルの「カスタム型」セクションを参照してください。
.DLLのファイルサイズには上限がありますか?
いいえ。ただし、弊社はこのファイルはそれほど大きくはないと考えています。
PhotonプラグインでPUNのPhotonNetwork.ServerTimestamp を取得する方法は?
Environment.TickCount
を使用します。