浏览代码

Cleaning up some of the lobby state so that leaving and rejoining/creating new lobbies works properly. Oh, also, I forgot to remove the ArePlayersReadyTime from the lobby, and also had a minor bug with the lobby heartbeat.

/main/staging
nathaniel.buck@unity3d.com 3 年前
当前提交
2a376941
共有 10 个文件被更改,包括 96 次插入53 次删除
  1. 4
      Assets/Scripts/Entities/GameStateManager.cs
  2. 83
      Assets/Scripts/Entities/LobbyUser.cs
  3. 6
      Assets/Scripts/Entities/LocalLobby.cs
  4. 5
      Assets/Scripts/Lobby/LobbyAsyncRequests.cs
  5. 4
      Assets/Scripts/Lobby/LobbyContentHeartbeat.cs
  6. 3
      Assets/Scripts/Lobby/ToLocalLobby.cs
  7. 18
      Assets/Scripts/Relay/RelayUtpClient.cs
  8. 5
      Assets/Scripts/Relay/RelayUtpHost.cs
  9. 19
      Assets/Scripts/Relay/RelayUtpSetup.cs
  10. 2
      Assets/Scripts/UI/InLobbyUserUI.cs

4
Assets/Scripts/Entities/GameStateManager.cs


void OnCreatedLobby()
{
m_localUser.IsHost = true;
OnJoinedLobby();
}

void OnLeftLobby()
{
m_localUser.Emote = EmoteType.None;
m_localUser.ResetState();
LobbyAsyncRequests.Instance.LeaveLobbyAsync(m_localLobby.LobbyID, ResetLocalLobby);
m_lobbyContentHeartbeat.EndTracking();
LobbyAsyncRequests.Instance.EndTracking();

void ResetLocalLobby()
{
m_localLobby.CopyObserved(new LocalLobby.LobbyData(), new Dictionary<string, LobbyUser>());
m_localLobby.AddPlayer(m_localUser); // As before, the local player will need to be plugged into UI before the lobby join actually happens.
m_localLobby.CountDownTime = 0;
m_localLobby.RelayServer = null;
}

83
Assets/Scripts/Entities/LobbyUser.cs


[Serializable]
public class LobbyUser : Observed<LobbyUser>
{
public LobbyUser(bool isHost = false, string displayName = null, string id = null, EmoteType emote = EmoteType.None, string userStatus = null)
public LobbyUser(bool isHost = false, string displayName = null, string id = null, EmoteType emote = EmoteType.None, UserStatus userStatus = UserStatus.Menu)
m_isHost = isHost;
m_displayName = displayName;
m_id = id;
m_emote = emote;
UserStatus status;
if (!string.IsNullOrEmpty(userStatus) && Enum.TryParse(userStatus, out status))
m_userStatus = status;
m_data = new UserData(isHost, displayName, id, emote, userStatus);
#region Local UserData
public struct UserData
{
public bool IsHost { get; set; }
public string DisplayName { get; set; }
public string ID { get; set; }
public EmoteType Emote { get; set; }
public UserStatus UserStatus { get; set; }
public UserData(bool isHost, string displayName, string id, EmoteType emote, UserStatus userStatus)
{
IsHost = isHost;
DisplayName = displayName;
ID = id;
Emote = emote;
UserStatus = userStatus;
}
}
private UserData m_data;
public void ResetState()
{
m_data = new UserData(false, m_data.DisplayName, m_data.ID, EmoteType.None, UserStatus.Menu); // ID and DisplayName should persist since this might be the local user.
}
#endregion
/// <summary>
/// Used for limiting costly OnChanged actions to just the members which actually changed.
/// </summary>

public UserMembers LastChanged => m_lastChanged;
bool m_isHost;
get { return m_isHost; }
get { return m_data.IsHost; }
if (m_isHost != value)
if (m_data.IsHost != value)
m_isHost = value;
m_data.IsHost = value;
m_lastChanged = UserMembers.IsHost;
OnChanged(this);
}

string m_displayName = "";
get => m_displayName;
get => m_data.DisplayName;
if (m_displayName != value)
if (m_data.DisplayName != value)
m_displayName = value;
m_data.DisplayName = value;
m_lastChanged = UserMembers.DisplayName;
OnChanged(this);
}

EmoteType m_emote = EmoteType.None;
get => m_emote;
get => m_data.Emote;
if (m_emote != value)
if (m_data.Emote != value)
m_emote = value;
m_data.Emote = value;
m_lastChanged = UserMembers.Emote;
OnChanged(this);
}

string m_id = "";
get => m_id;
get => m_data.ID;
if (m_id != value)
if (m_data.ID != value)
m_id = value;
m_data.ID = value;
m_lastChanged = UserMembers.ID;
OnChanged(this);
}

}
}
public override void CopyObserved(LobbyUser oldObserved)
public override void CopyObserved(LobbyUser observed)
UserData data = observed.m_data;
(m_displayName == oldObserved.m_displayName ? 0 : (int)UserMembers.DisplayName) |
(m_emote == oldObserved.m_emote ? 0 : (int)UserMembers.Emote) |
(m_id == oldObserved.m_id ? 0 : (int)UserMembers.ID) |
(m_isHost == oldObserved.m_isHost ? 0 : (int)UserMembers.IsHost) |
(m_userStatus == oldObserved.m_userStatus ? 0 : (int)UserMembers.UserStatus);
(m_data.DisplayName == data.DisplayName ? 0 : (int)UserMembers.DisplayName) |
(m_data.Emote == data.Emote ? 0 : (int)UserMembers.Emote) |
(m_data.ID == data.ID ? 0 : (int)UserMembers.ID) |
(m_data.IsHost == data.IsHost ? 0 : (int)UserMembers.IsHost) |
(m_data.UserStatus == data.UserStatus ? 0 : (int)UserMembers.UserStatus);
m_displayName = oldObserved.m_displayName;
m_emote = oldObserved.m_emote;
m_id = oldObserved.m_id;
m_isHost = oldObserved.m_isHost;
m_userStatus = oldObserved.m_userStatus;
m_data = data;
m_lastChanged = (UserMembers)lastChanged;
OnChanged(this);

6
Assets/Scripts/Entities/LocalLobby.cs


public bool Private { get; set; }
public int MaxPlayerCount { get; set; }
public LobbyState State { get; set; }
public long? AllPlayersReadyTime { get; set; }
public LobbyData(LobbyData existing)
{

Private = existing.Private;
MaxPlayerCount = existing.MaxPlayerCount;
State = existing.State;
AllPlayersReadyTime = existing.AllPlayersReadyTime;
}
public LobbyData(string lobbyCode)

Private = false;
MaxPlayerCount = -1;
State = LobbyState.Lobby;
AllPlayersReadyTime = null;
}
}

}
}
public long? AllPlayersReadyTime => m_data.AllPlayersReadyTime;
/// <summary>
/// Checks if we have n players that have the Status.
/// -1 Count means you need all Lobbyusers

OnChanged(this);
}
// This ends up being called from the lobby list when we get data about a lobby without having joined it yet.
public override void CopyObserved(LocalLobby oldObserved)
{
CopyObserved(oldObserved.Data, oldObserved.m_LobbyUsers);

5
Assets/Scripts/Lobby/LobbyAsyncRequests.cs


void OnLeftLobby(Response response)
{
onComplete?.Invoke();
// TEMP. As of 6/31/21, the lobbies service doesn't automatically delete emptied lobbies, though that functionality is expected in the near-term.
// Until then, we'll do a delete request whenever we leave, and if it's invalid, we'll just get a 403 back.
LobbyAPIInterface.DeleteLobbyAsync(lobbyId, null);
}
}

4
Assets/Scripts/Lobby/LobbyContentHeartbeat.cs


{
if (m_isAwaitingQuery || m_localLobby == null)
return;
LobbyAsyncRequests.Instance.DoLobbyHeartbeat(dt);
if (m_localUser.IsHost)
LobbyAsyncRequests.Instance.DoLobbyHeartbeat(dt);
m_isAwaitingQuery = true; // Note that because we make async calls, if one of them fails and doesn't call our callback, this will never be reset to false.
if (m_shouldPushData)
PushDataToLobby();

Dictionary<string, string> data = new Dictionary<string, string>();
data.Add("RelayCode", lobby.RelayCode);
data.Add("State", ((int)lobby.State).ToString());
// We only want the ArePlayersReadyTime to be set when we actually are ready for it, and it's null otherwise. So, don't set that here.
return data;
}

3
Assets/Scripts/Lobby/ToLocalLobby.cs


MaxPlayerCount = lobby.MaxPlayers,
RelayCode = lobby.Data?.ContainsKey("RelayCode") == true ? lobby.Data["RelayCode"].Value : null, // TODO: Remove?
State = lobby.Data?.ContainsKey("State") == true ? (LobbyState) int.Parse(lobby.Data["State"].Value) : LobbyState.Lobby, // TODO: Consider TryParse, just in case (and below). Although, we don't have fail logic anyway...
AllPlayersReadyTime = lobby.Data?.ContainsKey("AllPlayersReady") == true ? long.Parse(lobby.Data["AllPlayersReady"].Value) : (long?)null // TODO: Remove
};
Dictionary<string, LobbyUser> lobbyUsers = new Dictionary<string, LobbyUser>(); // TODO: So, right, why this?

}
}
// If the player isn't connected to Relay, or if we just don't know about them yet, get the most recent data that the lobby knows. // TODO: Relay should handle the latter case.
// If the player isn't connected to Relay, or if we just don't know about them yet, get the most recent data that the lobby knows.
// (If we have no local representation of the player, that gets added by the LocalLobby.)
LobbyUser incomingData = new LobbyUser(); // TODO: This is unclear; this should be just a data object replacing that of the user, not a new user whose data are taken.
incomingData.IsHost = lobby.HostId.Equals(player.Id);

18
Assets/Scripts/Relay/RelayUtpClient.cs


protected virtual void Uninitialize()
{
m_localUser.onChanged -= OnLocalChange;
Leave();
Locator.Get.UpdateSlow.Unsubscribe(UpdateSlow);
}
public void OnDestroy()

ProcessNetworkEventDataAdditional(conn, strm, msgType, id);
}
else if (cmd == NetworkEvent.Type.Disconnect)
ProcessDisconnectEvent(conn, strm);
protected virtual void ProcessDisconnectEvent(NetworkConnection conn, DataStreamReader strm)
{
// The host disconnected, and Relay does not support host migration. So, all clients should disconnect.
Debug.LogError("Host disconnected! Leaving the lobby.");
Leave();
Locator.Get.Messenger.OnReceiveMessage(MessageType.ChangeGameState, GameState.JoinMenu);
}
unsafe private string ReadLengthAndString(ref DataStreamReader strm)
{
byte length = strm.ReadByte();

}
}
}
}
public void Leave()
{
foreach (NetworkConnection connection in m_connections)
connection.Disconnect(m_networkDriver);
m_localLobby.RelayServer = null;
}
}
}

5
Assets/Scripts/Relay/RelayUtpHost.cs


CheckIfAllUsersReady();
}
protected override void ProcessDisconnectEvent(NetworkConnection conn, DataStreamReader strm)
{
// TODO: If a client disconnects, see if remaining players are all already ready.
}
public void OnReceiveMessage(MessageType type, object msg)
{
if (type == MessageType.LobbyUserStatus)

19
Assets/Scripts/Relay/RelayUtpSetup.cs


public class RelayUtpSetupHost : RelayUtpSetup
{
[Flags]
private enum JoinState { None = 0, Bound = 1, Joined = 2 }
private JoinState m_joinState = JoinState.None;
protected override void JoinRelay()
{
RelayInterface.AllocateAsync(m_localLobby.MaxPlayerCount, OnAllocation);

private void OnJoin(JoinAllocation joinAllocation)
{
m_localLobby.RelayServer = new ServerAddress(joinAllocation.RelayServer.IpV4, joinAllocation.RelayServer.Port);
m_joinState |= JoinState.Joined;
CheckForComplete();
}
protected override void OnBindingComplete()

else
{
Debug.LogWarning("Server is now listening!");
m_joinState |= JoinState.Bound;
CheckForComplete();
}
}
private void CheckForComplete()
{
if (m_joinState == (JoinState.Joined | JoinState.Bound))
{
m_isRelayConnected = true;
RelayUtpHost host = gameObject.AddComponent<RelayUtpHost>();
host.Initialize(m_networkDriver, m_connections, m_localUser, m_localLobby);

private void OnJoin(JoinAllocation allocation)
{
if (allocation == null)
return; // TODO: Error messaging.
return;
BindToAllocation(allocation.RelayServer.IpV4, allocation.RelayServer.Port, allocation.AllocationIdBytes, allocation.ConnectionData, allocation.HostConnectionData, allocation.Key, 1);
m_localLobby.RelayServer = new ServerAddress(allocation.RelayServer.IpV4, allocation.RelayServer.Port);
}

private IEnumerator ConnectToServer()
{
// Once the client is bound to the Relay server, you can send a connection request
// Once the client is bound to the Relay server, send a connection request.
m_connections.Add(m_networkDriver.Connect(m_endpointForServer));
while (m_networkDriver.GetConnectionState(m_connections[0]) == NetworkConnection.State.Connecting)
{

2
Assets/Scripts/UI/InLobbyUserUI.cs


case UserStatus.Connecting:
return "<color=#F0E442>Connecting...</color>"; // Bright Yellow
case UserStatus.InGame:
return "<color=#005500>In Game</color>"; //Orange
return "<color=#005500>In Game</color>"; // Green
default:
return "";
}

正在加载...
取消
保存