-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
425 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace HouraiTeahouse.Networking { | ||
|
||
/// <summary> | ||
/// An object for creating a local implementation of HouraiNetworking lobbies. | ||
/// | ||
/// Note that this class doesn't actually derive from Lobby: that would be | ||
/// LocalLobbyView. | ||
/// | ||
/// Primary use of this is for testing, but can also be used for establishing | ||
/// local play using netplay-only codebase. | ||
/// </summary> | ||
public sealed class LocalLobby : IDisposable { | ||
|
||
readonly MetadataContainer _metadata; | ||
readonly Dictionary<AccountHandle, LocalLobbyView> _connectedViews; | ||
readonly double _packetLossPercent; | ||
readonly Random _packetLossRng; | ||
|
||
public ulong Id { get; } | ||
public LobbyType Type { get; set; } | ||
public ulong OwnerId { get; private set; } | ||
public int Capacity { get; set; } | ||
|
||
/// <summary> | ||
/// Creates a local lobby and provides you the connected owner's lobby. | ||
/// </summary> | ||
/// <param name="id">the lobby's ID</param> | ||
/// <param name="capacity">how many player can connect.</param> | ||
/// <param name="ownerId">the owner of the lobby.</param> | ||
/// <param name="packetLossPercent">for unreliable packets, how often </param> | ||
/// <param name="packetLossRng">a System.Random instance for randomly producing packet loss</param> | ||
public static LocalLobbyView Create(ulong id, int capacity, ulong ownerId = 0, | ||
double packetLossPercent = 0, Random packetLossRng = null) { | ||
packetLossRng = packetLossRng ?? new Random(); | ||
var lobby = new LocalLobby(id, capacity, ownerId, packetLossPercent, packetLossRng); | ||
LocalLobbyView owner = lobby.CreateView(ownerId); | ||
owner.Join().Wait(); | ||
return owner; | ||
} | ||
|
||
LocalLobby(ulong id, int capacity, ulong ownerId, | ||
double packetLossPercent, Random packetLossRng) { | ||
Id = id; | ||
Capacity = capacity; | ||
_packetLossRng = packetLossRng; | ||
_connectedViews = new Dictionary<AccountHandle, LocalLobbyView>(); | ||
_metadata = new MetadataContainer(); | ||
} | ||
|
||
/// <summary> | ||
/// Creates a view of the lobby, as seen from a remote player. | ||
/// | ||
/// This does not connect the view to the lobby. LobbyView.Join must be called | ||
/// to "connect" the view to to the lobby. | ||
/// </summary> | ||
/// <param name="id">the user to create the view from</param> | ||
/// <returns>the Lobby view from the view of that player</returns> | ||
public LocalLobbyView CreateView(AccountHandle id) { | ||
if (_connectedViews.TryGetValue(id, out LocalLobbyView view)) { | ||
return view; | ||
} | ||
return new LocalLobbyView(id, this); | ||
} | ||
|
||
/// <summary> | ||
/// Deletes the lobby. This will invalidate the state of all related | ||
/// LocalLobbyViews and the associated LobbyMembers. | ||
/// </summary> | ||
public void Delete() => Dispose(); | ||
|
||
internal void Connect(LocalLobbyView view) { | ||
var handle = new AccountHandle(view.Id); | ||
if (_connectedViews.ContainsKey(handle)) return; | ||
_metadata.AddMember(handle); | ||
_connectedViews[handle] = view; | ||
foreach (var connectedView in _connectedViews.Values) { | ||
connectedView.Members.Add(handle); | ||
} | ||
} | ||
|
||
internal void Disconnect(LocalLobbyView view) { | ||
var handle = new AccountHandle(view.Id); | ||
if (!_connectedViews.ContainsKey(handle)) return; | ||
_metadata.RemoveMember(handle); | ||
_connectedViews.Remove(handle); | ||
foreach (var connectedView in _connectedViews.Values) { | ||
connectedView.Members.Remove(handle); | ||
} | ||
} | ||
|
||
internal void SendLobbyMessage(AccountHandle source, FixedBuffer msg) { | ||
foreach (var view in _connectedViews.Values) { | ||
if (view.Members.TryGetValue(source, out LobbyMember member)) { | ||
view.DispatchLobbyMessage(member, msg); | ||
} | ||
} | ||
} | ||
|
||
internal void SendNetworkMessage(AccountHandle source, AccountHandle target, FixedBuffer msg, Reliability reliability) { | ||
// Simulate unreliablity | ||
if (reliability == Reliability.Unreliable && _packetLossRng.NextDouble() > _packetLossPercent) { | ||
return; | ||
} | ||
LocalLobbyView view; | ||
LobbyMember member; | ||
if (!_connectedViews.TryGetValue(target, out view) || | ||
!view.Members.TryGetValue(source, out member)) { | ||
return; | ||
} | ||
member.DispatchNetworkMessage(msg); | ||
} | ||
|
||
internal IEnumerable<AccountHandle> GetMemberIds() => _connectedViews.Keys; | ||
internal string GetMetadata(string key) => _metadata.GetMetadata(key); | ||
|
||
internal void SetMetadata(string key, string value) { | ||
if (_metadata.SetMetadata(key, value)) { | ||
DispatchUpdate(); | ||
} | ||
} | ||
|
||
internal void DeleteMetadata(string key) { | ||
if (_metadata.DeleteMetadata(key)) { | ||
DispatchUpdate(); | ||
} | ||
} | ||
|
||
internal string GetMemberMetadata(AccountHandle handle, string key) => | ||
_metadata.GetMemberMetadata(handle, key); | ||
|
||
internal void SetMemberMetadata(AccountHandle handle, string key, string value) { | ||
if (_metadata.SetMemberMetadata(handle, key, value)) { | ||
DispatchMemberUpdate(handle); | ||
} | ||
} | ||
|
||
internal void DeleteMemberMetadata(AccountHandle handle, string key) { | ||
if (_metadata.DeleteMemberMetadata(handle, key)) { | ||
DispatchMemberUpdate(handle); | ||
} | ||
} | ||
|
||
internal IReadOnlyDictionary<string, string> GetAllMetadata() => _metadata.GetAllMetadata(); | ||
|
||
void DispatchUpdate() { | ||
foreach (var view in _connectedViews.Values) { | ||
view.DispatchUpdate(); | ||
} | ||
} | ||
|
||
void DispatchMemberUpdate(AccountHandle handle) { | ||
foreach (var view in _connectedViews.Values) { | ||
if (view.Members.TryGetValue(handle, out LobbyMember member)) { | ||
member.DispatchUpdate(); | ||
} | ||
} | ||
} | ||
|
||
public void Dispose() { | ||
foreach (var view in _connectedViews.Values) { | ||
view.Dispose(); | ||
} | ||
} | ||
|
||
} | ||
|
||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
|
||
namespace HouraiTeahouse.Networking { | ||
|
||
/// <summary> | ||
/// A Lobby implementation that does not make any networked calls. | ||
/// | ||
/// Unlike other lobby implementations, this does not do any permissions checking | ||
/// when calling metadata mutation calls. | ||
/// | ||
/// Primary use of this is for testing, but can also be used for establishing | ||
/// local play using netplay-only codebase. | ||
/// | ||
/// See also: <seealso cref="HouraiTeahouse.Networking.LocalLobby"/> | ||
/// </summary> | ||
public sealed class LocalLobbyView : Lobby { | ||
|
||
/// <summary> | ||
/// The underlying LocalLobby. | ||
/// </summary> | ||
public LocalLobby BaseLobby { get; } | ||
|
||
public override ulong Id { get; } | ||
public override ulong UserId { get; } | ||
public override ulong OwnerId => BaseLobby.OwnerId; | ||
public override LobbyType Type => BaseLobby.Type; | ||
public override int Capacity { | ||
get => BaseLobby.Capacity; | ||
set => BaseLobby.Capacity = value; | ||
} | ||
|
||
internal LocalLobbyView(ulong userId, LocalLobby lobby) : base() { | ||
UserId = userId; | ||
BaseLobby = lobby; | ||
Members.Refresh(); | ||
} | ||
|
||
public override int MemberCount => Members.Count; | ||
internal override IEnumerable<AccountHandle> GetMemberIds() => BaseLobby.GetMemberIds(); | ||
public override string GetMetadata(string key) => BaseLobby.GetMetadata(key); | ||
public override void SetMetadata(string key, string value) => BaseLobby.SetMetadata(key, value); | ||
public override void DeleteMetadata(string key) => BaseLobby.DeleteMetadata(key); | ||
public override IReadOnlyDictionary<string, string> GetAllMetadata() => BaseLobby.GetAllMetadata(); | ||
|
||
internal override string GetMemberMetadata(AccountHandle handle, string key) => | ||
throw new NotSupportedException(); | ||
internal override void SetMemberMetadata(AccountHandle handle, string key, string value) => | ||
throw new NotSupportedException(); | ||
internal override void DeleteMemberMetadata(AccountHandle handle, string key) => | ||
throw new NotSupportedException(); | ||
|
||
public override void SendLobbyMessage(FixedBuffer message) { | ||
BaseLobby.SendLobbyMessage(new AccountHandle(UserId), message); | ||
} | ||
|
||
internal override void SendNetworkMessage(AccountHandle target, FixedBuffer message, | ||
Reliability reliability = Reliability.Reliable) { | ||
BaseLobby.SendNetworkMessage(new AccountHandle(UserId), target, message, reliability); | ||
} | ||
|
||
public override Task Join() { | ||
BaseLobby.Connect(this); | ||
return Task.CompletedTask; | ||
} | ||
|
||
public override void Leave() { | ||
BaseLobby.Disconnect(this); | ||
Dispose(); | ||
} | ||
|
||
public override void Delete() => Dispose(); | ||
|
||
} | ||
|
||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.