// 定数 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 座標チャンネル入室パターン const string CHANNEL_JOIN_TYPE_USE_URL_ROOMID = "roomid"; const string CHANNEL_JOIN_TYPE_RANDOM_CHANNEL = "random"; const string CHANNEL_JOIN_TYPE_SELECT_ANY_CHANNEL = "select"; const string CHANNEL_JOIN_TYPE_CREATE_NEW_RANDOM_CHANNEL = "createrandom"; const string CHANNEL_JOIN_TYPE_REDIRECT_TO_OUTGAME = "redirect"; const string CHANNEL_JOIN_TYPE_CUSTOM = "custom"; const string CHANNEL_JOIN_TYPE_NONE = "none"; delegate void JoinChannelDelegate(JsVal); delegate bool CheckAvailableDelegate(JsVal); JoinChannelDelegate g_FirstJoinFunc; JoinChannelDelegate g_FailedJoinFunc; CheckAvailableDelegate g_IsAvailableFunc; string g_authorityJwt; string g_ticketJwt; string g_AfterJwtChannelId; bool g_RequiredTickets; fVoidCallback g_ReConnectCallback; class ChSBridge { // 入室ダイアログ処理を通ったか bool m_DecidedEnterFlag; // ワールド情報のFetchが終わったか bool m_LoadedWorldInfo; // プロフィールの初期化が終わったか bool m_InitedProfile; // 初回入室処理を行ったか bool m_FirstJoined; bool m_AllowMultiVoiceChannel; ChSModelSingle m_SubSingleModel; ChSModelMultiple m_SubMultipleModel; VCCModel m_VCCModel; string m_ReConnectChannelId; int m_FailedCount; ChSErrorDialog m_ErrorDialog; public ChSBridge() { m_DecidedEnterFlag = false; m_LoadedWorldInfo = false; m_InitedProfile = false; m_FirstJoined = false; m_AllowMultiVoiceChannel = false; m_SubSingleModel = system.Layer_GetComponentByName("vcc_icon_only_base"); m_SubMultipleModel = system.Layer_GetComponentByName("vcc_icon_only_base"); m_VCCModel = system.Layer_GetComponentByName("vcc_icon_only_base"); // ChS-APIの初期化 JsVal options = makeJsObj(); options.AddPropertyByVal("enableHeartbeatInterval", makeJsBoolFrom(false)); heliport.v3.api.channelSessionApi.initChannelSession(_initChannelSessionCallback, options); // FirstEnterをHeliScript側で行うように設定する hsNetSetScriptControllableEnter(true); m_FailedCount = 0; m_ErrorDialog = new ChSErrorDialog(); hel_net_init_mic_device(); } public void OnNetEstablishmentFail(string ErrorCode) { // エラーが発生したのでエラーダイアログを表示 m_ErrorDialog.ShowFailedConnection(ErrorCode); } public void OnNetConnectionError(string ErrorCode) { // エラーが発生したのでエラーダイアログを表示 m_ErrorDialog.ShowFailedConnection(ErrorCode); } public void OnNetOperationError(string ErrorCode) { // エラーが発生したのでエラーダイアログを表示 m_ErrorDialog.ShowFailedConnection(ErrorCode); } // ChSModelSingle._CreatePosChannelCallback()、ChSModelMultiple._CreatePosChannelCallback() から呼び出される共通処理 public void HandlePositionChannelCreationError(string ErrorCode) { m_ErrorDialog.ShowFailedCreatePositionChanel(ErrorCode); } public void SetInitedProfile(string param) { m_InitedProfile = (param == "true"); } public void Update() { if(!m_FirstJoined) { // 入室ダイアログ・ワールド情報のFetch・プロフィールの初期化が終わっていれば初回入室処理を実行する if(m_DecidedEnterFlag && m_LoadedWorldInfo && m_InitedProfile) { m_FirstJoined = true; // 初回入室処理 if(m_AllowMultiVoiceChannel) { m_SubMultipleModel.FirstEnterPosChannel(); } else { m_SubSingleModel.FirstEnterChannel(); } } } } public void OnDecidedEnterDialog() { m_DecidedEnterFlag = true; } public bool IsAllowMultiVoiceChannel() { return m_AllowMultiVoiceChannel; } void _initChannelSessionCallback(JsVal val) { ReturnType result = hsLoadReturnType(val); if(!result.IsValid()) return; heliport.v3.api.channelSessionApi.fetchChannelSession(_fetchChannelSessionCallback); } void _fetchChannelSessionCallback(JsVal val) { ReturnType result = hsLoadReturnType(val); if(!result.IsValid()) return; string channel_session_code = result.data.GetProperty("channel_session").GetProperty("channel_session_code").GetStr(); m_VCCModel.SetSelfChannelSessionCode(channel_session_code); // heliport.v3.api.channelSessionApi.fetchWorldDetail(_fetchWorldDetailCallback, hsNetGetSpatiumCode(), hsGetCurrentWorldId()); } void _fetchWorldDetailCallback(JsVal val) { ReturnType result = hsLoadReturnType(val); if(!result.IsValid()) return; // カーネリアンのような複数音声チャンネルを許可するか m_AllowMultiVoiceChannel = result.data.GetProperty("world").GetProperty("allow_multi_voice_channel").GetBool(); if(m_AllowMultiVoiceChannel) { // 複数ボイスチャンネルようのGUIをオンにする hsCanvasResetToggleDefault("Toggle_VCC_VCh_Single_Multiple"); hsCanvasToggleChange("Toggle_VCC_VCh_Single_Multiple"); } // 入退出処理の登録 if(m_AllowMultiVoiceChannel) { m_SubMultipleModel.RegistJointProcess(); } else { m_SubSingleModel.RegistJointProcess(); } m_LoadedWorldInfo = true; // プロフィールを初期化する // initChannelSession → プロフィール取得 → 初回チャンネル入室 // の順番で必ず行わないと、ゲストプロフィールが取得できなくなったり(名前がguestのまま・プロフィールが何も描画されない)、 // 入室時にプレゼンターかどうかの判定ができなくなってしまう hsCallCanvasComponentMethod("config_profile", "ProfileMenuViewModel", "InitProfile"); } // ChannelSessionを初期化し再接続する public void ReConnectIfJoining(fVoidCallback callback) { VCCChannelData CurrentChannel = null; if(m_AllowMultiVoiceChannel) { CurrentChannel = m_SubMultipleModel.GetCurrentPosChannel(); } else { CurrentChannel = m_SubSingleModel.GetCurrentChannel(); } if(CurrentChannel === null) { callback(); return; } m_ReConnectChannelId = CurrentChannel.ChannelId; // 一度退出する if(m_AllowMultiVoiceChannel) { ExitSpace(); } else { ExitChannel(); } // ChannelSessionを初期化する g_ReConnectCallback = callback; JsVal options = makeJsObj(); options.AddPropertyByVal("enableHeartbeatInterval", makeJsBoolFrom(false)); heliport.v3.api.channelSessionApi.initChannelSession(_initChannelSession4ReConnect, options); } void _initChannelSession4ReConnect(JsVal val) { ReturnType result = hsLoadReturnType(val); if(!result.IsValid()) return; heliport.v3.api.channelSessionApi.fetchChannelSession(_fetchChannelSessionCallback4ReConnect); } void _fetchChannelSessionCallback4ReConnect(JsVal val) { ReturnType result = hsLoadReturnType(val); if(!result.IsValid()) return; string channel_session_code = result.data.GetProperty("channel_session").GetProperty("channel_session_code").GetStr(); m_VCCModel.SetSelfChannelSessionCode(channel_session_code); // if(!m_ReConnectChannelId.IsEmpty()) { string ChannelId = m_ReConnectChannelId; if(!ChannelId.IsEmpty()) { // 再入室する if(m_AllowMultiVoiceChannel) { m_SubMultipleModel.EnterPosChannel(ChannelId); } else { m_SubSingleModel.EnterChannel(ChannelId); } } m_ReConnectChannelId = ""; } if(!g_ReConnectCallback.Empty()) { g_ReConnectCallback(); g_ReConnectCallback.Clear(); } } // 音声チャンネルに再接続する public void ReConnectVoiceChannel() { if(m_AllowMultiVoiceChannel) { VCCChannelData CurrentVoiceChannel = m_SubMultipleModel.GetCurrentVoiceChannel(); if(CurrentVoiceChannel !== null) { string ChannelId = CurrentVoiceChannel.ChannelId; m_SubMultipleModel.EnterVoiceChannel(ChannelId); } } else { VCCChannelData CurrentChannel = m_SubSingleModel.GetCurrentChannel(); if(CurrentChannel !== null) { string ChannelId = CurrentChannel.ChannelId; m_SubSingleModel.EnterChannel(ChannelId); } } } // 座標チャンネル入室処理 public void DoHelJoinPosChannel(string ChannelId, bool RequiredTickets) { g_AfterJwtChannelId = ChannelId; g_RequiredTickets = RequiredTickets; // authorityJwtのチェック _CheckAuthorityJwt(ChannelId); } void _HelJoinPosChannel() { // チケット必須ワールドでTicketJwtが無効な時はアウトゲームに飛ばす if(g_RequiredTickets && g_ticketJwt.IsEmpty()) { m_ErrorDialog.ShowFailedJoin("RTC01010105"); ResetJwt(); return; } hel_net_join_pos_channel(_JoinPosChannelCallback, g_AfterJwtChannelId, g_authorityJwt, g_ticketJwt); ResetJwt(); } void _JoinPosChannelCallback(JsVal val) { if(val === null || val.IsNull()) return; VCCPositionChannel Channel = new VCCPositionChannel(); if(!Channel.Analyse(val, false)) return; if(!Channel.Data.IsValid) { m_FailedCount++; // 入室に失敗 hsSystemOutputLog("[Error] Failed to Join PosChannel - ErrorCode: %s\n" % Channel.Data.ErrorCode); // 無限ループに陥る可能性があるので試行回数は2回まで if(m_FailedCount < 2) { m_ErrorDialog.ShowFailedJoin(Channel.Data.ErrorCode); } return; } m_FailedCount = 0; } void ResetJwt() { // Jwt情報をリセットする g_authorityJwt = ""; g_ticketJwt = ""; g_AfterJwtChannelId = ""; g_RequiredTickets = false; } void _CheckAuthorityJwt(string ChannelId) { if(ChannelId.IsEmpty()) { hsSystemOutputLog("[Error] Failed to check AuthorityJwt because ChannelId is empty.\n"); return; } heliport.v3.api.conferenceManagerApi.fetchPositionChannelAuthorityJwtString(_CheckAuthorityJwtCallback, hsNetGetSpatiumCode(), hsGetCurrentWorldId(), ChannelId); } void _CheckAuthorityJwtCallback(JsVal val) { ReturnType result = hsLoadReturnType(val); Player SelfPlayer = hsPlayerGet(); string SelfUserType = SelfPlayer.GetCustomState("UserType"); // authorityJwtが使えるのはログインユーザーだけである if(result.IsValid() && SelfUserType == "login") { g_authorityJwt = result.data.GetStr(); } else { g_authorityJwt = ""; } // ticketJwtのチェック _CheckTicketJwt(); } void _CheckTicketJwt() { if(g_RequiredTickets) { heliport.v3.api.conferenceManagerApi.fetchTicketJwt(_FetchTicketJwtCallback, hsNetGetSpatiumCode(), hsGetCurrentWorldId(), g_AfterJwtChannelId); } else { // TicketJwtのチェックは不要なのですぐにIHeliNetworkのJoinを実行する g_ticketJwt = ""; _HelJoinPosChannel(); } } void _FetchTicketJwtCallback(JsVal val) { ReturnType result = hsLoadReturnType(val); if(!result.IsValid()) { // TicketJwtの取得に失敗したのでJwtは空として入室処理を実行する g_ticketJwt = ""; _HelJoinPosChannel(); } else { g_ticketJwt = result.data.GetProperty("jwt").GetStr();; _HelJoinPosChannel(); } } public void FetchPosChannelList(int PagerIndex, fJsValCallback callback, bool UseOffset) { if(m_AllowMultiVoiceChannel) { m_SubMultipleModel.FetchPosChannelList(PagerIndex, callback, UseOffset); } } public void FetchChannelList(int PagerIndex, fJsValCallback callback, bool UseOffset) { if(m_AllowMultiVoiceChannel) { m_SubMultipleModel.FetchVoiceChannelList(PagerIndex, callback, UseOffset); } else { m_SubSingleModel.FetchChannelList(PagerIndex, callback, UseOffset); } } public void FetchSpaceDetail(fJsValCallback callback) { if(m_AllowMultiVoiceChannel) { VCCChannelData CurrentChannel = m_SubMultipleModel.GetCurrentPosChannel(); } } public void FetchChannelDetail(fJsValCallback callback) { if(m_AllowMultiVoiceChannel) { VCCChannelData CurrentChannel = m_SubMultipleModel.GetCurrentVoiceChannel(); if(CurrentChannel !== null) { string ChannelId = CurrentChannel.ChannelId; if(!ChannelId.IsEmpty()) { hel_net_get_voice_channel(callback, ChannelId); } } } else { VCCChannelData CurrentChannel = m_SubSingleModel.GetCurrentChannel(); if(CurrentChannel !== null) { string ChannelId = CurrentChannel.ChannelId; if(!ChannelId.IsEmpty()) { hel_net_get_position_channel(callback, ChannelId); } } } } public void EnterSpace(string ChannelId) { if(m_AllowMultiVoiceChannel) { m_SubMultipleModel.EnterPosChannel(ChannelId); } } public void EnterChannel(string ChannelId) { if(m_AllowMultiVoiceChannel) { m_SubMultipleModel.EnterVoiceChannel(ChannelId); } else { m_SubSingleModel.EnterChannel(ChannelId); } } public void ExitSpace() { if(m_AllowMultiVoiceChannel) { m_SubMultipleModel.ExitPosChannel(); } } public void ExitChannel() { if(m_AllowMultiVoiceChannel) { m_SubMultipleModel.ExitVoiceChannel(); } else { m_SubSingleModel.ExitChannel(); } } public void CreateNewChannel(string ChannelName, string SelectedChannelType) { if(m_AllowMultiVoiceChannel) { m_SubMultipleModel.CreateVoiceNewChannel(ChannelName, SelectedChannelType); } else { m_SubSingleModel.CreateNewChannel(ChannelName, SelectedChannelType); } } public void UpdateChannel(string ChannelName, string SelectedChannelType) { if(m_AllowMultiVoiceChannel) { m_SubMultipleModel.UpdateVoiceChannel(ChannelName, SelectedChannelType); } else { m_SubSingleModel.UpdateChannel(ChannelName, SelectedChannelType); } } public bool IsChannelCreator(string UserCode) { return (m_AllowMultiVoiceChannel)? m_SubMultipleModel.IsChannelCreator(UserCode) : m_SubSingleModel.IsChannelCreator(UserCode); } public VCCChannelData GetCurrentChannel() { VCCChannelData CurrentChannel = null; if(m_AllowMultiVoiceChannel) { CurrentChannel = m_SubMultipleModel.GetCurrentVoiceChannel(); } else { CurrentChannel = m_SubSingleModel.GetCurrentChannel(); } return CurrentChannel; } public bool IsConnected() { bool val = false; if(m_AllowMultiVoiceChannel) { val = m_SubMultipleModel.IsConnected(); } else { val = m_SubSingleModel.IsConnected(); } return val; } public void UpdateSpaceName(string SpaceName) { if(m_AllowMultiVoiceChannel) { m_SubMultipleModel.UpdateSpaceName(SpaceName); } } public void UpdateChannelName(string ChannelName) { if(m_AllowMultiVoiceChannel) { m_SubMultipleModel.UpdateChannelName(ChannelName); } else { m_SubSingleModel.UpdateChannelName(ChannelName); } } public void ClearUserTable() { m_VCCModel.ClearUserTable(); } public string GetSelfUserCode() { return m_VCCModel.GetSelfUserCode(); } public void UpdateCreateUserCode(string UserCode) { m_VCCModel.UpdateCreateUserCode(UserCode); } public void InitUserTable(VCCChannelData Channel) { m_VCCModel.InitUserTable(Channel); } public void AddSelfVoiceID(string VoiceID) { m_VCCModel.AddSelfVoiceID(VoiceID); } }