fVoidCallback g_ExitPositionChannelCallback; component ChSModel { ChSBridge m_ChSBridge; ChSJoinProcess m_JoinProcess; VCCChannelData m_CurrentPositionChannel; VCCChannelData m_CurrentVoiceChannel; bool m_IsConnected; string m_TempHelConnectChannelId; bool m_TempRequiredTickets; string m_TempVoiceChannelId; public ChSModel() { m_ChSBridge = system.Layer_GetComponentByName("vcc_icon_only_base"); m_JoinProcess = system.Layer_GetComponentByName("vcc_icon_only_base"); m_CurrentPositionChannel = null; m_CurrentVoiceChannel = null; m_IsConnected = false; } // 初回入室処理と入室失敗処理を登録 public void RegistJointProcess() { // 初回座標チャンネル入室処理 switch (hsNetGetFirstPosJoin()) { case "": // 未指定時 case CHANNEL_JOIN_TYPE_USE_URL_ROOMID: g_FirstJoinFunc = m_JoinProcess.JoinFromURLRoomID; break; case CHANNEL_JOIN_TYPE_BY_LOBBYING_CHANNEL: g_FirstJoinFunc = m_JoinProcess.JoinPositionChannelByLobbying; break; case CHANNEL_JOIN_TYPE_RANDOM_CHANNEL: g_FirstJoinFunc = m_JoinProcess.JoinRandomPositionChannel; break; case CHANNEL_JOIN_TYPE_SELECT_ANY_CHANNEL: g_FirstJoinFunc = m_JoinProcess.JoinSelectedAnyRoom; break; case CHANNEL_JOIN_TYPE_CREATE_NEW_RANDOM_CHANNEL: g_FirstJoinFunc = m_JoinProcess.JoinNewRoom; break; case CHANNEL_JOIN_TYPE_CUSTOM: g_FirstJoinFunc = m_JoinProcess.FirstJoinCustom; break; } // 座標チャンネル入室失敗時の処理 switch (hsNetGetFailedPosJoin()) { case "": // 未指定時 case CHANNEL_JOIN_TYPE_BY_LOBBYING_CHANNEL: g_FailedJoinFunc = m_JoinProcess.JoinPositionChannelByLobbying; break; case CHANNEL_JOIN_TYPE_RANDOM_CHANNEL: g_FailedJoinFunc = m_JoinProcess.JoinRandomPositionChannel; break; case CHANNEL_JOIN_TYPE_SELECT_ANY_CHANNEL: g_FailedJoinFunc = m_JoinProcess.JoinSelectedAnyRoom; break; case CHANNEL_JOIN_TYPE_CREATE_NEW_RANDOM_CHANNEL: g_FailedJoinFunc = m_JoinProcess.JoinNewRoom; break; case CHANNEL_JOIN_TYPE_REDIRECT_TO_OUTGAME: g_FailedJoinFunc = m_JoinProcess.RedirectToOutGame; break; case CHANNEL_JOIN_TYPE_CUSTOM: g_FirstJoinFunc = m_JoinProcess.FailedJoinCustom; break; } } // Getter・Setter ////////////////////////////////////////////////////////////////////////////////////////////////// public bool IsConnected() { return m_IsConnected; } public bool IsChannelCreator(string selfUserCode) { if ((m_ChSBridge.IsAllowMultiVoiceChannel() ? m_CurrentVoiceChannel : m_CurrentPositionChannel) === null) { return false; } string createUserCode = (m_ChSBridge.IsAllowMultiVoiceChannel() ? m_CurrentVoiceChannel : m_CurrentPositionChannel).CreateUserCode; return !createUserCode.IsEmpty() && createUserCode == selfUserCode; } public VCCChannelData GetCurrentPositionChannel() { return m_CurrentPositionChannel; } public VCCChannelData GetCurrentVoiceChannel() { return m_CurrentVoiceChannel; } public void UpdateSpaceName(string spaceName) { if(m_CurrentPositionChannel === null) { return; } m_CurrentPositionChannel.ChannelName = spaceName; // ひとまずViewModelにはまだ通知しない } public void UpdateChannelName(string channelName) { VCCChannelData currentChannel = m_ChSBridge.GetCurrentChannel(); if(currentChannel === null) { return; } currentChannel.ChannelName = channelName; VCCViewModel viewModel = system.Layer_GetComponentByName("vcc_icon_only_base"); if(viewModel !== null) { viewModel.UpdateConnectStatus(currentChannel.ChannelName, 0, true, false); } } // 座標チャンネルリストの取得/////////////////////////////////////////////////////////////////////////////////////// public void FetchPositionChannelList(int pagerIndex, fJsValCallback callback, bool useOffset) { int offset = 0; int limit = -1; if (useOffset) { limit = m_ChSBridge.IsAllowMultiVoiceChannel() ? 6 : 5; offset = limit * pagerIndex; } hel_net_get_position_channel_list( callback, hsNetGetSpatiumCode(), hsGetCurrentWorldId(), offset, limit, hsNetExcludeEmptyChannel() ); } // 座標チャンネル入室処理 ////////////////////////////////////////////////////////////////////////////////////////// public void FirstEnterPositionChannel() { JsVal val = makeJsObj(); g_FirstJoinFunc(val); } public void EnterPositionChannel(string channelId) { if (!channelId.IsEmpty()) { hel_net_get_position_channel(_GetPosChannelCallback, channelId); } else { JsVal val = makeJsObj(); g_FailedJoinFunc(val); } } void _GetPosChannelCallback(JsVal val) { if (val === null || val.IsNull()) { return; } VCCPositionChannel channel = new VCCPositionChannel(); if (!channel.Analyse(val, false)) { return; } if (!channel.Data.IsValid) { // 入室に失敗 hsSystemOutputLog("[Error] Failed to Join PosChannel - ErrorCode: %s\n" % channel.Data.ErrorCode); JsVal val = makeJsObj(); g_FailedJoinFunc(val); return; } // エンジンRTC接続処理 CallHelPositionConnectProcess(channel); } // 座標チャンネル退出処理 ////////////////////////////////////////////////////////////////////////////////////////// public void ExitPositionChannel(fVoidCallback callback) { g_ExitPositionChannelCallback.Clear(); if (m_ChSBridge.IsAllowMultiVoiceChannel()) { hel_net_leave_pos_channel(callback); m_CurrentPositionChannel = null; } else { g_ExitPositionChannelCallback = callback; // エンジンRTCの切断 hel_net_disconnect(_LeaveAfterDisconnectPosCallback); } } void _LeaveAfterDisconnectPosCallback() { hel_skyway_set_roomid(""); if (!g_ExitPositionChannelCallback.IsEmpty()) { g_ExitPositionChannelCallback(); g_ExitPositionChannelCallback.Clear(); } } // 座標チャンネル操作 ////////////////////////////////////////////////////////////////////////////////////////////// public void CreateNewPositionChannel(string channelName, string selectedChannelType) { hel_net_create_pos_channel( _CreatePosChannelCallback, hsNetGetSpatiumCode(), hsGetCurrentWorldId(), selectedChannelType, channelName, "" ); } void _CreatePosChannelCallback(JsVal val) { if (val === null || (val.GetType() != 1 && val.GetType() != 2)) { return; } VCCPositionChannel channel = new VCCPositionChannel(); if(!channel.Analyse(val, false)) { return; } if (channel.Data.ErrorCode != "") { // 座標チャンネルの作成に失敗 m_ChSBridge.HandlePositionChannelCreationError(channel.Data.ErrorCode); return; } // 誰も入室していない部屋はChannelList並ばなくなるのでChannel作成後は即座に入室する // エンジンRTC接続処理 CallHelPositionConnectProcess(channel); } public void UpdatePositionChannel(string channelName, string selectedChannelType) { hel_net_update_pos_channel(_UpdatePosChannelCallback, selectedChannelType, channelName, ""); } void _UpdatePosChannelCallback(JsVal val) { if (val === null || (val.GetType() != 1 && val.GetType() != 2)) { return; } m_CurrentPositionChannel.ChannelName = val.GetProperty("name").GetStr(); if (!m_ChSBridge.IsAllowMultiVoiceChannel()) { VCCViewModel viewModel = system.Layer_GetComponentByName("vcc_icon_only_base"); if(viewModel !== null) { viewModel.UpdateConnectStatus(m_CurrentPositionChannel.ChannelName, 0, true, true); } } // チャンネル情報が更新されたことを通知する hsNetSendCustomData( m_ChSBridge.IsAllowMultiVoiceChannel() ? "VCC_Update_Space" : "VCC_Update_Channel", m_CurrentPositionChannel.ChannelName ); } // 音声チャンネルリスト取得 //////////////////////////////////////////////////////////////////////////////////////// public void FetchVoiceChannelList(int pagerIndex, fJsValCallback callback, bool useOffset) { string positionChannelId; if (m_CurrentPositionChannel !== null) { positionChannelId = m_CurrentPositionChannel.ChannelId; } if (positionChannelId.IsEmpty()) { return; } int offset = 0; int limit = -1; if (useOffset) { limit = 5; offset = limit * pagerIndex; } hel_net_get_voice_channel_list(callback, positionChannelId, offset, limit, hsNetExcludeEmptyChannel()); } // 音声チャンネル入室処理 ////////////////////////////////////////////////////////////////////////////////////////// public void EnterVoiceChannel(string channelId) { // エンジンRTC接続処理 CallHelVoiceConnectProcess(channelId); } // 音声チャンネル退出処理 ////////////////////////////////////////////////////////////////////////////////////////// public void ExitVoiceChannel() { hel_net_leave_voice_channel(_LeaveVoiceCallback); } void _LeaveVoiceCallback() { } public void ExitVoiceChannelAfterJoinDefaultVoiceChannel() { hel_net_leave_voice_channel(_LeaveVoiceAfterJoinDefaultVoiceChanneCallback); } void _LeaveVoiceAfterJoinDefaultVoiceChanneCallback() { hel_net_join_default_voice_channel(_JoinDefaultVoiceCallback); } void _JoinDefaultVoiceCallback() { } // 音声チャンネル操作 ////////////////////////////////////////////////////////////////////////////////////////////// public void CreateNewVoiceChannel(string channelName, string selectedChannelType) { string positionChannelId; if(m_CurrentPositionChannel !== null) { positionChannelId = m_CurrentPositionChannel.ChannelId; } if (positionChannelId.IsEmpty()) { return; } hel_net_create_voice_channel( _CreateVoiceChannelCallback, positionChannelId, selectedChannelType, channelName, "" ); } void _CreateVoiceChannelCallback(JsVal val) { // 誰も入室していない部屋はChannelList並ばなくなるのでChannel作成後は即座に入室する if(val === null || (val.GetType() != 1 && val.GetType() != 2)) { return; } CallHelVoiceConnectProcess(val.GetProperty("channel_id").GetStr()); } public void UpdateVoiceChannel(string channelName, string selectedChannelType) { hel_net_update_voice_channel(_UpdateVoiceChannelCallback, selectedChannelType, channelName, ""); } void _UpdateVoiceChannelCallback(JsVal val) { if (val === null || (val.GetType() != 1 && val.GetType() != 2)) { return; } m_CurrentVoiceChannel.ChannelName = val.GetProperty("name").GetStr(); VCCViewModel viewModel = system.Layer_GetComponentByName("vcc_icon_only_base"); if (viewModel !== null) { viewModel.UpdateConnectStatus(m_CurrentVoiceChannel.ChannelName, 0, true, true); } // チャンネル情報が更新されたことを通知する hsNetSendCustomData("VCC_Update_Channel", m_CurrentVoiceChannel.ChannelName); } // エンジンRTC入室処理 ///////////////////////////////////////////////////////////////////////////////////////////// public void OnNetPosConnected() { // 入室するチャンネルの詳細情報を取得する JsVal netCurrentChannel = hel_net_get_current_pos_channel(); if (netCurrentChannel === null || netCurrentChannel.IsNull()) { return; } // 入室するチャンネルの詳細情報を取得する VCCPositionChannel channel = new VCCPositionChannel(); if (!channel.Analyse(netCurrentChannel, false)) { return; } m_CurrentPositionChannel = channel.Data; if (!m_ChSBridge.IsAllowMultiVoiceChannel()) { // チャンネル情報編集UIの表示切替 hsCanvasResetToggleDefault("Toggle_VCC_Is_Channel_Creator"); if(!m_CurrentPositionChannel.CreateUserCode.IsEmpty() && m_ChSBridge.GetSelfUserCode() == m_CurrentPositionChannel.CreateUserCode) { hsCanvasToggleChange("Toggle_VCC_Is_Channel_Creator"); } } if(m_ChSBridge.IsAllowMultiVoiceChannel() && m_CurrentVoiceChannel !== null && m_CurrentVoiceChannel.HasGlobalVC) { // グローバルボイスチャットに入室している } else { // VCCViewのステータスを更新する VCCViewModel viewModel = system.Layer_GetComponentByName("vcc_icon_only_base"); if(viewModel !== null) { if (m_ChSBridge.IsAllowMultiVoiceChannel()) { // まだVoiceChannelには入室していないので空のチャンネル情報を渡す viewModel.UpdateConnectStatus("", -1, false, true); } else { viewModel.UpdateConnectStatus(m_CurrentPositionChannel.ChannelName, 0, true, true); } } // 入室した旨を伝える LayerBundle layer = hsLayerGet("toast"); if(layer !== null) { layer.CallComponentMethod("ToastViewModel", "SetNoticeTime", "250,3500,250"); string messageTemplate; if (system.IsLangJA()) { messageTemplate = "『%s』に参加しました"; } else { messageTemplate = "Join \"%s\""; } layer.CallComponentMethod( "ToastViewModel", "NotifyMessage", messageTemplate % m_CurrentPositionChannel.ChannelName ); } if(hsSystemIsVRMode()) { hsCanvasSetLayerShow( "hud_webxr", true); } } if (!m_ChSBridge.IsAllowMultiVoiceChannel()) { // 接続中 m_IsConnected = true; } //KickBan通知設定 { LayerBundle layer = hsLayerGet("prof_option_layer"); if (layer !== null) { Func ActivateKickBanNotification = system.Layer_GetComponentMethod( layer.GetCurrent()._GetInstanceID(), "ProfRejector", "ActivateKickBanNotification" ); if (!ActivateKickBanNotification.IsEmpty()) { ActivateKickBanNotification(); } } } // テキストチャットを閉じて初期化する { LayerBundle layer = hsLayerGet("textchat_common"); if (layer !== null) { layer.CallComponentMethod("TCViewModel", "ExitTextChat", ""); } } } public void OnNetVoiceConnected(string voiceId) { if (m_ChSBridge.IsAllowMultiVoiceChannel()) { // 入室するチャンネルの詳細情報を取得する JsVal netCurrentChannel = hel_net_get_current_voice_channel(); if (netCurrentChannel === null || netCurrentChannel.IsNull()) { return; } // 入室するチャンネルの詳細情報を取得する VCCVoiceChannel channel = new VCCVoiceChannel(); if (!channel.Analyse(netCurrentChannel, false)) { return; } // カレントチャンネルを更新 m_CurrentVoiceChannel = channel.Data; // チャンネル情報編集UIの表示切替 hsCanvasResetToggleDefault("Toggle_VCC_Is_Channel_Creator"); if (!m_CurrentVoiceChannel.CreateUserCode.IsEmpty() && m_ChSBridge.GetSelfUserCode() == m_CurrentVoiceChannel.CreateUserCode) { hsCanvasToggleChange("Toggle_VCC_Is_Channel_Creator"); } } // ユーザーテーブルを初期化する m_ChSBridge.InitUserTable( m_ChSBridge.IsAllowMultiVoiceChannel() ? m_CurrentVoiceChannel : m_CurrentPositionChannel ); if (m_ChSBridge.IsAllowMultiVoiceChannel()) { // VCCViewのステータスを更新する VCCViewModel viewModel = system.Layer_GetComponentByName("vcc_icon_only_base"); if(viewModel !== null) { viewModel.UpdateConnectStatus(m_CurrentVoiceChannel.ChannelName, 0, true, true); } // 入室した旨を伝える string message; if(system.IsLangJA()) { message = "『" + m_CurrentVoiceChannel.ChannelName + "』" + "に参加しました"; } else { message = "Join" + "\"" + m_CurrentVoiceChannel.ChannelName + "\""; } LayerBundle layer = hsLayerGet("toast"); if(layer !== null) { layer.CallComponentMethod("ToastViewModel", "SetNoticeTime", "250,3500,250"); layer.CallComponentMethod("ToastViewModel", "NotifyMessage", message); } } // 接続中 m_IsConnected = true; // 自身のVoiceIDを追加 m_ChSBridge.AddSelfVoiceID(voiceId); // もし発言ユーザかつマイクがオンだった場合は、マイクオフをスキップする if (!hel_net_live_get_mic_enabled()) { // マイクをオフにする hel_net_set_mic_enabled(false); hsNetSetMicIconState(false); } hsCanvasResetToggleDefault("Toggle_Voicechat_Mic_On_Off"); // 自身のネームプレートのマイクアイコンをミュート表示に hel_setText(ETextParamType_SetMicState, "0"); } public void OnNetPosDisconnected() { if (!m_ChSBridge.IsAllowMultiVoiceChannel()) { ChannelDisconnectedCallback(); } } public void OnNetVoiceDisconnected() { // マイクのアイコン表示をオフにする hsNetSetMicIconState(false); if (m_ChSBridge.IsAllowMultiVoiceChannel()) { ChannelDisconnectedCallback(); } } void ChannelDisconnectedCallback() { LayerBundle layer = hsLayerGet("toast"); if (layer !== null) { layer.CallComponentMethod("ToastViewModel", "SetNoticeTime", "250,3500,250"); string message = system.IsLangJA() ? "ボイスチャットグループから退出しました" : "Left the voice chat group"; layer.CallComponentMethod("ToastViewModel", "NotifyMessage", message); } m_IsConnected = false; if (m_ChSBridge.IsAllowMultiVoiceChannel()) { m_CurrentVoiceChannel = null; } else { m_CurrentPositionChannel = null; } // VCCViewのステータスを更新する VCCViewModel viewModel = system.Layer_GetComponentByName("vcc_icon_only_base"); if (viewModel !== null) { viewModel.UpdateConnectStatus("", -1, false, true); } // ユーザーテーブルをリセットする m_ChSBridge.ClearUserTable(); } void CallHelPositionConnectProcess(VCCPositionChannel channel) { m_TempHelConnectChannelId = channel.Data.ChannelId; m_TempRequiredTickets = channel.Data.RequiredTickets; // エンジンRTCの切断 hel_net_disconnect(_NetDisconnectCallback); } void _NetDisconnectCallback() { hel_skyway_set_roomid(""); // skywayはRoomIDが必須 hel_skyway_set_roomid(m_TempHelConnectChannelId); // エンジン側のRoomIDとURLを更新 hel_net_replace_room_url_with_roomid(makeJsStrFrom(m_TempHelConnectChannelId)); hel_net_set_rtc_provider(_NetSetRtcProviderCallback, m_TempHelConnectChannelId); } void _NetSetRtcProviderCallback() { // エンジン接続処理 hel_net_connect(_NetConnectCallback); } void _NetConnectCallback() { // 入室処理を実行 DoHelJoinPositionChannel(m_TempHelConnectChannelId, m_TempRequiredTickets); } void CallHelVoiceConnectProcess(string channelId) { m_TempVoiceChannelId = channelId; // エンジンRTCの切断 hel_net_leave_voice_channel(_JoinVoiceAfterLeave); } void _JoinVoiceAfterLeave() { hel_net_get_voice_channel(_GetVoiceChannelCallback, m_TempVoiceChannelId); } void _GetVoiceChannelCallback(JsVal val) { if (val === null || (val.GetType() != 1 && val.GetType() != 2)) { return; } // 入室するチャンネルの詳細情報を取得する VCCVoiceChannel channel = new VCCVoiceChannel(); if (!channel.Analyse(val, false)) { return; } // 入室処理を実行 hel_net_join_voice_channel(_ConnectVoiceProcess, m_TempVoiceChannelId); m_TempVoiceChannelId = ""; } void _ConnectVoiceProcess(JsVal val) { } void DoHelJoinPositionChannel(string channelId, bool requiredTickets) { m_ChSBridge.DoHelJoinPositionChannel(channelId, requiredTickets); m_TempHelConnectChannelId = ""; m_TempRequiredTickets = false; } }