This commit is contained in:
Даниил Заикин
2026-06-17 20:44:53 +03:00
parent 9d153773c2
commit b133e7c656
1880 changed files with 244545 additions and 0 deletions
@@ -0,0 +1,274 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Unity.Properties;
using Unity.Services.Core;
using Unity.Services.Multiplayer;
using UnityEngine;
using UnityEngine.UIElements;
namespace Blocks.Sessions
{
public class SessionBrowserViewModel : INotifyBindablePropertyChanged, IDataSourceViewHashProvider, IDisposable
{
private SessionObserver m_SessionObserver;
private ServiceObserver<IMultiplayerService> m_ServiceObserver;
private long m_UpdateVersion;
private bool m_SelectedAndAvailable;
private bool m_CanRefresh;
private int m_SelectedSessionIndex;
private ISession m_Session;
private List<SessionInfoViewModel> m_Sessions;
[CreateProperty]
public List<SessionInfoViewModel> Sessions
{
get => m_Sessions;
set
{
if (m_Sessions == value)
{
return;
}
m_Sessions = value;
++m_UpdateVersion;
Notify();
}
}
[CreateProperty]
public int SelectedSessionIndex
{
get => m_SelectedSessionIndex;
set
{
if (value >= 0 && value < Sessions.Count)
{
m_SelectedSessionIndex = value;
SelectedAndAvailable = true;
}
else
{
SelectedAndAvailable = false;
}
}
}
public string GetSelectedSessionId()
{
if (SelectedSessionIndex >= 0 && SelectedSessionIndex < Sessions.Count)
{
return Sessions[SelectedSessionIndex].Id;
}
return null;
}
[CreateProperty]
public bool SelectedAndAvailable
{
get => m_SelectedAndAvailable;
set
{
var newValue = value;
if (value && m_Session != null && m_Session.Id == GetSelectedSessionId())
{
newValue = false;
}
if (m_SelectedAndAvailable != newValue)
{
m_SelectedAndAvailable = newValue;
++m_UpdateVersion;
Notify();
}
}
}
[CreateProperty]
public bool CanRefresh
{
get => m_CanRefresh;
set
{
if (m_CanRefresh == value)
{
return;
}
m_CanRefresh = value;
++m_UpdateVersion;
Notify();
}
}
public SessionBrowserViewModel(string sessionType)
{
Sessions = new List<SessionInfoViewModel>();
m_SessionObserver = new SessionObserver(sessionType);
m_SessionObserver.SessionAdded += OnSessionAdded;
if (m_SessionObserver.Session != null)
{
OnSessionAdded(m_SessionObserver.Session);
}
// This can be null while in edit mode and needs to be checked before creating the Observer.
if (UnityServices.Instance != null)
{
m_ServiceObserver = new ServiceObserver<IMultiplayerService>();
if (m_ServiceObserver.Service != null)
{
CanRefresh = true;
}
else
{
CanRefresh = false;
m_ServiceObserver.Initialized += OnServicesInitialized;
}
}
}
public async Task JoinSessionAsync(JoinSessionOptions options)
{
try
{
await MultiplayerService.Instance.JoinSessionByIdAsync(GetSelectedSessionId(), options);
}
catch (Exception exception)
{
Debug.LogException(exception);
}
}
void OnServicesInitialized(IMultiplayerService service)
{
m_ServiceObserver.Initialized -= OnServicesInitialized;
CanRefresh = true;
}
internal async Task UpdateSessionListAsync(int numberOfMaxSessions)
{
// if there is no connection to MultiplayerService, do not try to refresh
if (!CanRefresh)
{
Debug.LogWarning("Cannot refresh session list." +
"Multiplayer Services are not initialized." +
"You can initialize them with default settings by adding a " +
"ServicesInitialization and PlayerAuthentication components in your scene.");
return;
}
try
{
CanRefresh = false;
var queryResult = await MultiplayerService.Instance
.QuerySessionsAsync(new QuerySessionsOptions
{
SortOptions = new List<SortOption>
{
new (SortOrder.Descending,SortField.Name)
}
});
// properly dispose the sessionInfo view models first
foreach (var session in Sessions)
{
session.Dispose();
}
Sessions.Clear();
for (var i = 0; (i < Math.Min(queryResult.Sessions.Count, numberOfMaxSessions)); i++)
{
Sessions.Add(new SessionInfoViewModel(queryResult.Sessions[i]));
}
++m_UpdateVersion;
CanRefresh = true;
// reset selection
SelectedSessionIndex = -1;
}
catch (Exception ex)
{
Debug.LogError($"Failed to update session list: {ex.Message}");
}
}
private void OnSessionAdded(ISession newSession)
{
m_Session = newSession;
m_Session.RemovedFromSession += OnSessionRemoved;
m_Session.Deleted += OnSessionRemoved;
if (m_Session.Id == GetSelectedSessionId())
{
SelectedAndAvailable = false;
}
}
private void OnSessionRemoved()
{
var lastSessionId = m_Session.Id;
CleanupSession();
if (lastSessionId == GetSelectedSessionId())
{
SelectedAndAvailable = true;
}
}
private void CleanupSession()
{
m_Session.RemovedFromSession -= OnSessionRemoved;
m_Session.Deleted -= OnSessionRemoved;
m_Session = null;
}
public async Task<ISession> JoinSessionByIdAsync(JoinSessionOptions joinSessionOptions)
{
return await MultiplayerService.Instance.JoinSessionByIdAsync(GetSelectedSessionId(), joinSessionOptions);
}
public void Dispose()
{
if (m_SessionObserver != null)
{
m_SessionObserver.SessionAdded -= OnSessionAdded;
m_SessionObserver.Dispose();
m_SessionObserver = null;
}
if (m_ServiceObserver != null)
{
m_ServiceObserver.Dispose();
m_ServiceObserver = null;
}
if (m_Session != null)
{
CleanupSession();
}
}
/// <summary>
/// This method is used by UIToolkit to determine if any data bound to the UI has changed.
/// Instead of hashing the data, an m_UpdateVersion counter is incremented when changes occur.
/// </summary>
public long GetViewHashCode() => m_UpdateVersion;
/// <summary>
/// Suggested implementation of INotifyBindablePropertyChanged from UIToolkit.
/// </summary>
public event EventHandler<BindablePropertyChangedEventArgs> propertyChanged;
private void Notify([CallerMemberName] string property = null)
{
propertyChanged?.Invoke(this, new BindablePropertyChangedEventArgs(property));
}
}
}