Skip to content

Commit

Permalink
New player faces can now be selected in multiplayer lobby
Browse files Browse the repository at this point in the history
  • Loading branch information
Pyrdacor committed Jun 3, 2020
1 parent 01bc5bb commit 53266b3
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 21 deletions.
3 changes: 3 additions & 0 deletions Freeserf.Core/Network/IServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public interface IServer
public delegate void ClientJoinedHandler(ILocalServer server, IRemoteClient client);
public delegate void ClientLeftHandler(ILocalServer server, IRemoteClient client);
public delegate bool GameReadyHandler(bool ready);
public delegate void ClientChangedFaceHandler(ILocalServer server, IRemoteClient client, PlayerFace face);

public interface ILocalServer : IServer, INetworkDataHandler
{
Expand All @@ -70,10 +71,12 @@ public interface ILocalServer : IServer, INetworkDataHandler
void DisconnectClient(IRemoteClient client, bool sendNotificationToClient);
void BroadcastHeartbeat();
void BroadcastDisconnect();
void BroadcastLobbyData();

event ClientJoinedHandler ClientJoined;
event ClientLeftHandler ClientLeft;
event GameReadyHandler GameReady;
event ClientChangedFaceHandler ClientChangedFace;

/// <summary>
/// List of all connected clients.
Expand Down
2 changes: 1 addition & 1 deletion Freeserf.Core/Network/RequestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public partial class Global
public const byte SpontaneousMessage = 0xff;

static byte CurrentMessageIndex = 0;
static object MessageIndexLock = new object();
static readonly object MessageIndexLock = new object();

public static byte GetNextMessageIndex()
{
Expand Down
10 changes: 10 additions & 0 deletions Freeserf.Core/Network/UserActionData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ public enum UserAction
/// </summary>
Unknown,
/// <summary>
/// Change player face (and color). In lobby only.
/// Byte 0: Face index
/// </summary>
ChangeFace,
/// <summary>
/// Change a game-relevant settings.
/// Byte 0: The setting (see <see cref="UserActionGameSetting"/>)
/// See <see cref="UserActionGameSetting"/> for additional bytes/parameters.
Expand Down Expand Up @@ -370,6 +375,11 @@ public void Send(IRemote destination)
destination.Send(rawData.ToArray());
}

internal static UserActionData CreateChangeFaceUserAction(byte number, PlayerFace face)
{
return new UserActionData(number, 0u, UserAction.ChangeFace, new byte[1] { (byte)face });
}

internal static UserActionData CreateChangeSettingUserAction(byte number, Game game, UserActionGameSetting setting, params byte[] values)
{
byte[] parameters = new byte[values.Length + 1];
Expand Down
95 changes: 90 additions & 5 deletions Freeserf.Core/UI/GameInitBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,7 @@ public void HandleAction(Action action)
Server.Init(checkBoxSameValues.Checked, checkBoxServerValues.Checked, ServerGameInfo.MapSize, randomInput.Text, ServerGameInfo.Players);
Server.ClientJoined += Server_ClientJoined;
Server.ClientLeft += Server_ClientLeft;
Server.ClientChangedFace += Server_ClientChangedFace;
break;
case Action.StartGame:
{
Expand Down Expand Up @@ -1027,6 +1028,7 @@ public void HandleAction(Action action)
{
Server.ClientJoined -= Server_ClientJoined;
Server.ClientLeft -= Server_ClientLeft;
Server.ClientChangedFace -= Server_ClientChangedFace;

GameManager.Instance.CloseGame();
interf = interf.Viewer.ChangeTo(Viewer.Type.Server).MainInterface;
Expand Down Expand Up @@ -1252,6 +1254,39 @@ public void HandleAction(Action action)
}
}

private void Server_ClientChangedFace(ILocalServer server, IRemoteClient client, PlayerFace face)
{
if (client == null || !playerClientMapping.Values.Contains(client) || client.PlayerIndex > ServerGameInfo.PlayerCount)
return;

if (face >= PlayerFace.You && face <= PlayerFace.FriendYellow)
{
bool failed = false;

lock (ServerGameInfo)
{
for (int i = 0; i < ServerGameInfo.PlayerCount; ++i)
{
if (i != client.PlayerIndex && CompareFace(ServerGameInfo.Players[i].Face, face))
{
failed = true;
break;
}

}

if (!failed)
{
ServerGameInfo.Players[(int)client.PlayerIndex].SetCharacter(face);
ServerUpdate();
SetRedraw();
}
}
}

server.BroadcastLobbyData();
}

private void Client_GameStarted(object sender, System.EventArgs e)
{
// TODO: remote spectator
Expand Down Expand Up @@ -1480,6 +1515,37 @@ PlayerInfo GetRandomPlayerInfo(uint playerIndex)
return playerInfo;
}

static readonly PlayerFace[] MultiplayerFaceOrder =
{
PlayerFace.You, PlayerFace.YouRed, PlayerFace.YouMagenta, PlayerFace.YouYellow,
PlayerFace.FriendBlue, PlayerFace.Friend, PlayerFace.FriendMagenta, PlayerFace.FriendYellow
};

static bool CompareFace(PlayerFace face1, PlayerFace face2)
{
if (face1 == face2)
return true;

// In multiplayer games the same color can't be taken twice.
if (face1 >= PlayerFace.You && face2 >= PlayerFace.You)
{
return face1 switch
{
PlayerFace.You => face2 == PlayerFace.FriendBlue,
PlayerFace.YouRed => face2 == PlayerFace.Friend,
PlayerFace.YouMagenta => face2 == PlayerFace.FriendMagenta,
PlayerFace.YouYellow => face2 == PlayerFace.FriendYellow,
PlayerFace.FriendBlue => face2 == PlayerFace.You,
PlayerFace.Friend => face2 == PlayerFace.YouRed,
PlayerFace.FriendMagenta => face2 == PlayerFace.YouMagenta,
PlayerFace.FriendYellow => face2 == PlayerFace.YouYellow,
_ => false
};
}

return false;
}

bool HandlePlayerClick(uint playerIndex, int cx, int cy)
{
if (cx < 8 || cx > 8 + 64 || cy < 8 || cy > 76)
Expand Down Expand Up @@ -1553,30 +1619,47 @@ bool HandlePlayerClick(uint playerIndex, int cx, int cy)
if (cx < 8 + 32 && cy < 72) // click on face
{
bool canNotChange = (playerIndex == 0 && gameType != GameType.AIvsAI) ||
gameType == GameType.MultiplayerJoined || // TODO: maybe later choose between some special faces
gameType == GameType.Mission ||
gameType == GameType.Tutorial ||
gameType == GameType.Load;

if (gameType == GameType.MultiplayerServer)
{
canNotChange = playerIndex != 0 && player.Face >= PlayerFace.You;
}
else if (gameType == GameType.MultiplayerJoined)
{
canNotChange = playerIndex != Client.PlayerIndex;
}

if (!canNotChange)
{
// Face
bool inUse;

do
{
uint next = ((uint)player.Face + 1) % 11; // Note: Use 12 here to also allow the last enemy as a custom game player
next = Math.Max(1u, next);
PlayerFace next;

if (gameType == GameType.MultiplayerServer || gameType == GameType.MultiplayerJoined)
{
int index = (MultiplayerFaceOrder.ToList().IndexOf(player.Face) + 1) % 8;
next = MultiplayerFaceOrder[index];
}
else
{
next = (PlayerFace)(((int)player.Face + 1) % 11); // Note: Use 12 here to also allow the last enemy as a custom game player
}

player.SetCharacter((PlayerFace)next);
player.SetCharacter(next);

// Check that face is not already in use by another player
inUse = false;

for (uint i = 0; i < ServerGameInfo.PlayerCount; ++i)
{
if (playerIndex != i &&
ServerGameInfo.GetPlayer(i).Face == (PlayerFace)next)
CompareFace(ServerGameInfo.GetPlayer(i).Face, next))
{
inUse = true;
break;
Expand All @@ -1587,6 +1670,8 @@ bool HandlePlayerClick(uint playerIndex, int cx, int cy)

if (gameType == GameType.MultiplayerServer)
ServerUpdate();
else if (gameType == GameType.MultiplayerJoined)
Client.SendUserAction(UserActionData.CreateChangeFaceUserAction(Network.Global.SpontaneousMessage, player.Face));
}
}
else if (cx >= 8 + 32 && cx < 8 + 32 + 8 && cy >= 8 && cy < 24) // click on copy values button
Expand Down
38 changes: 23 additions & 15 deletions Freeserf.Network/Server.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ public GameInfo GameInfo
public event ClientJoinedHandler ClientJoined;
public event ClientLeftHandler ClientLeft;
public event GameReadyHandler GameReady;
public event ClientChangedFaceHandler ClientChangedFace;

public void Run(bool useServerValues, bool useSameValues, uint mapSize, string mapSeed,
IEnumerable<PlayerInfo> players, CancellationToken cancellationToken)
Expand Down Expand Up @@ -517,9 +518,7 @@ void HandleData(RemoteClient client, byte[] data)
{
case ServerState.Lobby:
{
// TODO allow user actions in lobby? can clients do something in lobby?

if (networkData.Type != NetworkDataType.Request)
if (networkData.Type != NetworkDataType.Request && networkData.Type != NetworkDataType.UserActionData)
{
client.SendResponse(networkData.MessageIndex, ResponseType.BadState);
throw new ExceptionFreeserf("Request expected.");
Expand Down Expand Up @@ -597,10 +596,19 @@ void ProcessData(IRemoteClient client, INetworkData networkData, ResponseHandler
{
case ServerState.Lobby:
{
// TODO: assert that it is a request (checked before in HandleData)
var request = networkData as RequestData;
if (networkData is RequestData request)
HandleLobbyRequest(client, request.MessageIndex, request.Request, responseHandler);
else if (networkData is UserActionData userAction)
{
if (userAction.UserAction != UserAction.ChangeFace)
{
Log.Error.Write(ErrorSystemType.Network, $"Received user action {userAction.UserAction} in lobby.");
responseHandler?.Invoke(ResponseType.BadState);
return;
}

HandleLobbyRequest(client, request.MessageIndex, request.Request, responseHandler);
ClientChangedFace?.Invoke(this, client, (PlayerFace)userAction.Parameters[0]);
}

break;
}
Expand Down Expand Up @@ -705,10 +713,10 @@ void HandleLobbyRequest(IRemoteClient client, byte messageIndex, Request request
case Request.LobbyData:
// TODO: check if the client just requested it (bruteforce attacks should be avoided)
lock (lobbyServerInfo)
lock (lobbyPlayerInfo)
{
client.SendLobbyDataUpdate(messageIndex, lobbyServerInfo, lobbyPlayerInfo);
}
lock (lobbyPlayerInfo)
{
client.SendLobbyDataUpdate(messageIndex, lobbyServerInfo, lobbyPlayerInfo);
}
break;
default:
responseHandler?.Invoke(ResponseType.BadRequest);
Expand Down Expand Up @@ -955,17 +963,17 @@ private void BroadcastResumeRequest()
Broadcast((client) => new RequestData(Global.SpontaneousMessage, Request.Resume).Send(client));
}

private void BroadcastLobbyData()
public void BroadcastLobbyData()
{
Log.Verbose.Write(ErrorSystemType.Network, $"Broadcast lobby data to {clients.Count} clients.");

Broadcast((client) =>
{
lock (lobbyServerInfo)
lock (lobbyPlayerInfo)
{
client.SendLobbyDataUpdate(Global.SpontaneousMessage, lobbyServerInfo, lobbyPlayerInfo);
}
lock (lobbyPlayerInfo)
{
client.SendLobbyDataUpdate(Global.SpontaneousMessage, lobbyServerInfo, lobbyPlayerInfo);
}
});
}

Expand Down
1 change: 1 addition & 0 deletions Issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- After saving the quit confirm will not ask for saving even if the game progressed a good amount of time
- After closing a popup the delayed click handler will be raised when the box is already closed and therefore will count as a map click.
This should be avoided somehow.
- New faces represent colors which should be adjusted accordingly in multiplayer games.


## Rendering
Expand Down

0 comments on commit 53266b3

Please sign in to comment.