Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 58 additions & 10 deletions apps/counterstrikesharp/src/FiveStack.Events/PlayerChat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,47 @@ namespace FiveStack;
public partial class FiveStackPlugin
{
public HookResult OnPlayerChat(CCSPlayerController? player, CommandInfo info)
{
return HandlePlayerChat(player, info, false);
}

public HookResult OnPlayerTeamChat(CCSPlayerController? player, CommandInfo info)
{
return HandlePlayerChat(player, info, true);
}

private HookResult HandlePlayerChat(
CCSPlayerController? player,
CommandInfo info,
bool teamOnly
)
{
if (player == null || !player.IsValid)
{
return HookResult.Continue;
}

string message = info.ArgString.Trim('"');

if (teamOnly)
{
RelayTeamChat(player, message);

// CSS skips the remaining say_team listeners once one returns
// Handled or Stop, and GagPlayer after this one is what blocks a
// gagged speaker in game and tells them why.
return HookResult.Continue;
}

if (player.Team == CsTeam.Spectator)
{
PublishChatEvent(player, info.ArgString.Trim('"'));
PublishChatEvent(player, message);

string clan = string.IsNullOrEmpty(player.Clan) ? "" : $"[{player.Clan}]";

_gameServer.Message(
HudDestination.Chat,
$" {ChatColors.Red}{clan}{ChatColors.White} {player.PlayerName}: {info.ArgString.Trim('"')}"
$" {ChatColors.Red}{clan}{ChatColors.White} {player.PlayerName}: {message}"
);

return HookResult.Stop;
Expand Down Expand Up @@ -57,20 +83,42 @@ public HookResult OnPlayerChat(CCSPlayerController? player, CommandInfo info)
}
}

PublishChatEvent(player, info.ArgString.Trim('"'));
PublishChatEvent(player, message);

return HookResult.Continue;
}

private void RelayTeamChat(CCSPlayerController player, string message)
{
MatchData? matchData = _matchService.GetCurrentMatch()?.GetMatchData();

if (matchData == null)
{
return;
}

(string Event, Dictionary<string, object> Data)? teamChat = MatchUtility.TeamChatEvent(
matchData,
player.SteamID.ToString(),
player.PlayerName,
message
);

if (teamChat == null)
{
return;
}

_matchEvents.PublishGameEvent(teamChat.Value.Event, teamChat.Value.Data);
}

private void PublishChatEvent(CCSPlayerController player, string message)
{
_matchEvents.PublishGameEvent(
"chat",
new Dictionary<string, object>
{
{ "player", player.SteamID.ToString() },
{ "message", message },
}
(string eventName, Dictionary<string, object> data) = MatchUtility.ChatEvent(
player.SteamID.ToString(),
message
);

_matchEvents.PublishGameEvent(eventName, data);
}
}
118 changes: 118 additions & 0 deletions apps/counterstrikesharp/src/FiveStack.Utilities/MatchUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,124 @@ string playerName
});
}

public static string? GetTeamChatRelayLineupId(
MatchData matchData,
string steamId,
string playerName
)
{
if (!matchData.relay_team_chat)
{
return null;
}

bool gagged =
GetMemberFromLineup(matchData, steamId, playerName)?.is_gagged == true
|| matchData
.lineup_1.lineup_players.Concat(matchData.lineup_2.lineup_players)
.Any(member => member.is_gagged && member.steam_id == steamId);

if (gagged)
{
return null;
}

return GetTeamChatLineupId(matchData, steamId, playerName);
}

// A lineup_1 placeholder whose name prefixes a lineup_2 player would
// otherwise claim them, so exact steam ids are matched before names.
public static string? GetTeamChatLineupId(
MatchData matchData,
string steamId,
string playerName
)
{
List<MatchMember> players = matchData
.lineup_1.lineup_players.Concat(matchData.lineup_2.lineup_players)
.ToList();

Guid lineupId =
players
.Find(member =>
!string.IsNullOrEmpty(member.steam_id) && member.steam_id == steamId
)
?.match_lineup_id
?? GetCoachLineupId(matchData, steamId)
?? players
.Find(member =>
member.steam_id == null && member.placeholder_name.StartsWith(playerName)
)
?.match_lineup_id
?? Guid.Empty;

if (lineupId == Guid.Empty)
{
return null;
}

return lineupId.ToString();
}

private static Guid? GetCoachLineupId(MatchData matchData, string steamId)
{
if (!matchData.options.coaches || string.IsNullOrEmpty(steamId))
{
return null;
}

if (matchData.lineup_1.coach_steam_id == steamId)
{
return matchData.lineup_1.id;
}

if (matchData.lineup_2.coach_steam_id == steamId)
{
return matchData.lineup_2.id;
}

return null;
}

public static (string Event, Dictionary<string, object> Data) ChatEvent(
string steamId,
string message
)
{
return (
"chat",
new Dictionary<string, object> { { "player", steamId }, { "message", message } }
);
}

// Team lines go out under their own event so an api that predates
// them drops them as unknown, instead of treating them as all chat and
// posting them where the other team reads.
public static (string Event, Dictionary<string, object> Data)? TeamChatEvent(
MatchData matchData,
string steamId,
string playerName,
string message
)
{
string? lineupId = GetTeamChatRelayLineupId(matchData, steamId, playerName);

if (lineupId == null)
{
return null;
}

return (
"teamChat",
new Dictionary<string, object>
{
{ "player", steamId },
{ "message", message },
{ "lineupId", lineupId },
}
);
}

// A client presenting the raw match password is a streamer, unless the
// lineup still has placeholder seats: then it may be the player
// meant to fill one.
Expand Down
1 change: 1 addition & 0 deletions apps/counterstrikesharp/src/FiveStackPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public override void Load(bool hotReload)
_gameServer.Ping(ModuleVersion);

AddCommandListener("say", OnPlayerChat, HookMode.Pre);
AddCommandListener("say_team", OnPlayerTeamChat, HookMode.Pre);

AddCommandListener("say", GagPlayer, HookMode.Pre);
AddCommandListener("say_team", GagPlayer, HookMode.Pre);
Expand Down
Loading
Loading