Skip to content

Commit 734eea9

Browse files
committed
Getting ready for V1.6.0
1 parent e0ac786 commit 734eea9

27 files changed

+892
-509
lines changed

UncomplicatedCustomTeams/API/Enums/WaveType.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ public enum WaveType
99
AfterDecontamination,
1010
UsedItem,
1111
RoundStarted,
12-
ScpDeath
12+
ScpDeath,
13+
TeamDependent,
14+
AfterGeneratorActivated
1315
}
1416
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using Discord;
2+
using System;
3+
using System.Text.Json.Serialization;
4+
5+
namespace UncomplicatedCustomTeams.API.Features
6+
{
7+
internal class LogEntry
8+
{
9+
/// <summary>
10+
/// Gets the time in unix milliseconds of the message
11+
/// </summary>
12+
public long Time { get; }
13+
14+
/// <summary>
15+
/// Gets the <see cref="Discord.LogLevel"/> or a custom LogLevel of the message
16+
/// </summary>
17+
public string Level { get; }
18+
19+
/// <summary>
20+
/// Gets the message of the log
21+
/// </summary>
22+
public string Content { get; }
23+
24+
#nullable enable
25+
/// <summary>
26+
/// Gets the custom error code of the message - can be null!
27+
/// </summary>
28+
public string? Error { get; }
29+
#nullable disable
30+
31+
/// <summary>
32+
/// Gets the instance of the Error as string
33+
/// </summary>
34+
public string PublicError => Error is null ? string.Empty : $"{Error} ";
35+
36+
[JsonIgnore]
37+
public DateTimeOffset DateTimeOffset => DateTimeOffset.FromUnixTimeMilliseconds(Time);
38+
39+
[JsonConstructor]
40+
public LogEntry(long time, string level, string content, string error = null)
41+
{
42+
Time = time;
43+
Level = level;
44+
Content = content;
45+
Error = error;
46+
}
47+
48+
public LogEntry(long time, LogLevel level, string content, string error = null) : this(time, level.ToString(), content, error) { }
49+
50+
public override string ToString() => $"[{DateTimeOffset.Year}-{DateTimeOffset.Month}-{DateTimeOffset.Day} {DateTimeOffset.Hour}:{DateTimeOffset.Minute}:{DateTimeOffset.Second} {DateTimeOffset.Offset}] [{Level}] [UncomplicatedCustomRoles] {PublicError}{Content}";
51+
}
52+
}

UncomplicatedCustomTeams/API/Features/SummonedTeam.cs

Lines changed: 108 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
using System;
66
using System.Collections.Generic;
77
using System.Linq;
8+
using System.Reflection;
89
using UncomplicatedCustomTeams.API.Enums;
910
using UncomplicatedCustomTeams.API.Storage;
1011
using UncomplicatedCustomTeams.Utilities;
12+
using UnityEngine;
1113
using Utils.NonAllocLINQ;
1214

1315
namespace UncomplicatedCustomTeams.API.Features
@@ -17,11 +19,12 @@ public class SummonedTeam
1719
/// <summary>
1820
/// Gets a list of every spawned <see cref="Team"/> as <see cref="SummonedTeam"/>
1921
/// </summary>
20-
public static List<SummonedTeam> List { get; } = new();
22+
public static List<SummonedTeam> List { get; } = [];
23+
public static event Action<SummonedTeam> OnTeamSummoned;
2124

2225
public string Id { get; }
2326

24-
public List<SummonedCustomRole> Players { get; } = new();
27+
public List<SummonedCustomRole> Players { get; } = [];
2528

2629
public Team Team { get; }
2730

@@ -77,36 +80,14 @@ public bool IsTeamEliminated()
7780
}
7881

7982
/// <summary>
80-
/// Checks if the given team is a custom team.
83+
/// Checks if a player is part of any spawned custom team.
8184
/// </summary>
82-
public static bool IsCustomTeam(PlayerRoles.Team team)
85+
public static bool IsPlayerInCustomTeam(Player player)
8386
{
84-
return Team.List.Any(t => t.Name == team.ToString());
85-
}
86-
87-
/// <summary>
88-
/// Checks if the round should end based on the alive teams and winning conditions.
89-
/// </summary>
90-
public static void CheckRoundEndCondition()
91-
{
92-
var aliveTeams = Player.List.Where(p => p.IsAlive)
93-
.Select(p => p.Role.Team)
94-
.Where(t => !IsCustomTeam(t))
95-
.Distinct()
96-
.ToList();
87+
if (player == null)
88+
return false;
9789

98-
var winningTeams = Team.GetWinningTeams();
99-
bool hasWinningTeamAlive = aliveTeams.Any(team => winningTeams.Contains(team));
100-
bool onlyWinningTeamsRemain = aliveTeams.All(team => winningTeams.Contains(team));
101-
bool hasAliveCustomTeam = SummonedTeam.List.Any(team => team.HasAlivePlayers());
102-
103-
if (hasWinningTeamAlive && onlyWinningTeamsRemain && hasAliveCustomTeam)
104-
{
105-
if (!Round.IsLocked)
106-
{
107-
Round.EndRound();
108-
}
109-
}
90+
return List.Any(summonedTeam => summonedTeam.Players.Any(summonedRole => summonedRole.Player.Id == player.Id));
11091
}
11192

11293
/// <summary>
@@ -186,7 +167,7 @@ public static SummonedTeam Summon(Team team, IEnumerable<Player> players)
186167
int totalAllowed = team.TeamRoles.Sum(r => r.MaxPlayers);
187168
int assigned = 0;
188169

189-
var random = new Random();
170+
var random = new System.Random();
190171
var roleQueue = team.TeamRoles
191172
.Where(r => r.Priority != RolePriority.None)
192173
.GroupBy(r => r.Priority)
@@ -229,6 +210,9 @@ public static SummonedTeam Summon(Team team, IEnumerable<Player> players)
229210
}
230211

231212
team.SpawnCount++;
213+
214+
OnTeamSummoned?.Invoke(SummonedTeam);
215+
232216
return SummonedTeam;
233217
}
234218

@@ -238,26 +222,109 @@ public static SummonedTeam Summon(Team team, IEnumerable<Player> players)
238222
/// </summary>
239223
private static IEnumerator<float> PlaySoundSequence(Team team)
240224
{
241-
AudioPlayer audioPlayer = AudioPlayer.CreateOrGet($"Global_Audio_{team.Id}", onIntialCreation: (p) =>
225+
Assembly audioAssembly = AppDomain.CurrentDomain.GetAssemblies()
226+
.FirstOrDefault(a => a.GetName().Name.Contains("AudioPlayer"));
227+
228+
if (audioAssembly == null)
229+
{
230+
LogManager.Warn("AudioPlayerApi assembly not found.");
231+
yield break;
232+
}
233+
234+
Type audioPlayerType = audioAssembly.GetTypes().FirstOrDefault(t => t.Name == "AudioPlayer" && t.IsClass);
235+
if (audioPlayerType == null)
242236
{
243-
p.AddSpeaker("Main", isSpatial: false, maxDistance: 5000f);
244-
});
245-
float volume = Clamp(team.SoundVolume, 1f, 100f);
237+
yield break;
238+
}
239+
240+
var createOrGetMethod = audioPlayerType.GetMethod("CreateOrGet", BindingFlags.Public | BindingFlags.Static);
241+
var addSpeakerMethod = audioPlayerType.GetMethods(BindingFlags.Public | BindingFlags.Instance)
242+
.FirstOrDefault(m => m.Name == "AddSpeaker" && m.GetParameters().Length == 5);
243+
var addClipMethod = audioPlayerType.GetMethod("AddClip", BindingFlags.Public | BindingFlags.Instance);
244+
245+
if (createOrGetMethod == null || addSpeakerMethod == null || addClipMethod == null)
246+
{
247+
yield break;
248+
}
249+
250+
string startClipId = null;
251+
int startIndex = -1;
252+
float startDelay = 0f;
246253

247254
for (int i = 0; i < team.SoundPaths.Count; i++)
248255
{
249-
var sound = team.SoundPaths[i];
256+
var s = team.SoundPaths[i];
257+
if (!string.IsNullOrEmpty(s.Path) && s.Path != "/path/to/your/ogg/file")
258+
{
259+
startClipId = $"sound_{team.Id}_{i}";
260+
startIndex = i;
261+
startDelay = s.Delay;
262+
break;
263+
}
264+
}
250265

251-
if (string.IsNullOrEmpty(sound.Path) || sound.Path == "/path/to/your/ogg/file")
252-
continue;
266+
if (startDelay > 0f)
267+
{
268+
yield return Timing.WaitForSeconds(startDelay);
269+
}
270+
271+
object audioPlayerInstance = null;
272+
try
273+
{
274+
LogManager.Debug($"Creating AudioPlayer with AutoPlay Clip: {startClipId ?? "NULL"}");
275+
276+
object[] parameters =
277+
[
278+
$"Global_Audio_{team.Id}",
279+
startClipId,
280+
null,
281+
true,
282+
true,
283+
null,
284+
(byte)0,
285+
null,
286+
null
287+
];
288+
289+
audioPlayerInstance = createOrGetMethod.Invoke(null, parameters);
290+
}
291+
catch (Exception ex)
292+
{
293+
LogManager.Error($"Failed to create Audio Player: {ex}");
294+
yield break;
295+
}
253296

254-
if (sound.Delay > 0f)
297+
float volume = team.SoundVolume;
298+
if (volume > 1.5f) volume /= 100f;
299+
volume = Mathf.Clamp(volume, 0.1f, 1.5f);
300+
301+
if (audioPlayerInstance != null)
302+
{
303+
try
255304
{
256-
yield return Timing.WaitForSeconds(sound.Delay);
305+
addSpeakerMethod.Invoke(audioPlayerInstance, ["Main", 1.0f, false, 0f, 5000f]);
257306
}
307+
catch (Exception) { }
308+
309+
for (int i = 0; i < team.SoundPaths.Count; i++)
310+
{
311+
if (i == startIndex) continue;
312+
313+
var sound = team.SoundPaths[i];
314+
if (string.IsNullOrEmpty(sound.Path) || sound.Path.Contains("/path/to/")) continue;
315+
316+
if (sound.Delay > 0f) yield return Timing.WaitForSeconds(sound.Delay);
258317

259-
string clipId = $"sound_{team.Id}_{i}";
260-
audioPlayer.AddClip(clipId, volume);
318+
try
319+
{
320+
string clipId = $"sound_{team.Id}_{i}";
321+
addClipMethod.Invoke(audioPlayerInstance, [clipId, volume, false, true]);
322+
}
323+
catch (Exception ex)
324+
{
325+
LogManager.Error($"Error queuing next clip: {ex.Message}");
326+
}
327+
}
261328
}
262329
}
263330

UncomplicatedCustomTeams/API/Features/UncomplicatedCustomRole.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ public class UncomplicatedCustomRole : UncomplicatedCustomRoles.API.Features.Cus
3333
public bool IsGodmodeEnabled { get; set; }
3434

3535
/// <summary>
36-
/// Whether bypassing obstacles is enabled for this role.
36+
/// Whether bypass is enabled for this role.
3737
/// </summary>
38-
[Description("Whether bypassing obstacles is enabled for this role.")]
38+
[Description("Whether bypass is enabled for this role.")]
3939
public bool IsBypassEnabled { get; set; }
4040

4141
/// <summary>

UncomplicatedCustomTeams/API/Storage/Bucket.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace UncomplicatedCustomTeams.API.Storage
55
{
66
internal class Bucket
77
{
8-
public static List<int> SpawnBucket { get; set; } = new();
8+
public static List<int> SpawnBucket { get; set; } = [];
99

1010
public static SummonedTeam Team { get; set; }
1111
}

UncomplicatedCustomTeams/Commands/LogShare.cs

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
using CommandSystem;
2-
using Newtonsoft.Json;
32
using System;
43
using System.Collections.Generic;
54
using System.Net;
6-
using System.Net.Http;
5+
using System.Text.Json;
6+
using System.Threading.Tasks;
77
using UncomplicatedCustomTeams.Utilities;
88

99
namespace UncomplicatedCustomTeams.Commands
@@ -15,9 +15,9 @@ internal class LogShare : ParentCommand
1515

1616
public override string Command { get; } = "uctlogs";
1717

18-
public override string[] Aliases { get; } = new string[] { };
18+
public override string[] Aliases { get; } = [];
1919

20-
public override string Description { get; } = "Share the UCT Debug logs with the developers.";
20+
public override string Description { get; } = "Share the UCR Debug logs with the developers";
2121

2222
public override void LoadGeneratedCommands() { }
2323

@@ -30,18 +30,26 @@ protected override bool ExecuteParent(ArraySegment<string> arguments, ICommandSe
3030
}
3131

3232
long Start = DateTimeOffset.Now.ToUnixTimeMilliseconds();
33+
response = $"Loading the JSON content to share with the developers...";
3334

34-
HttpStatusCode Response = LogManager.SendReport(out HttpContent Content);
35-
Dictionary<string, string> Data = JsonConvert.DeserializeObject<Dictionary<string, string>>(Plugin.HttpManager.RetriveString(Content));
36-
37-
if (Response is HttpStatusCode.OK && Data.ContainsKey("id"))
38-
{
39-
response = $"Successfully shared the UCT logs with the developers!\nSend this Id to the developers: {Data["id"]}\n\nTook {DateTimeOffset.Now.ToUnixTimeMilliseconds() - Start}ms";
40-
}
41-
else
35+
Task.Run(() =>
4236
{
43-
response = $"Failed to share the UCT logs with the developers: Server says: {Response}";
44-
}
37+
HttpStatusCode Response = LogManager.SendReport(out string content, arguments.Count > 0);
38+
try
39+
{
40+
if (Response is HttpStatusCode.OK)
41+
{
42+
Dictionary<string, string> Data = JsonSerializer.Deserialize<Dictionary<string, string>>(content);
43+
LogManager.Info($"[ShareTheLog] Successfully shared the UCT logs with the developers!\nSend this Id to the developers: {Data["id"]}\n\nTook {DateTimeOffset.Now.ToUnixTimeMilliseconds() - Start}ms");
44+
}
45+
else
46+
LogManager.Info($"Failed to share the UCT logs with the developers: Server says: {Response}");
47+
}
48+
catch (Exception e)
49+
{
50+
LogManager.Error(e.ToString());
51+
}
52+
});
4553

4654

4755
return true;

UncomplicatedCustomTeams/Commands/Owner.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using CommandSystem;
22
using System.Collections.Generic;
33
using System.Net;
4+
using UncomplicatedCustomRoles.Extensions;
45
using UncomplicatedCustomTeams.Interfaces;
56

67
namespace UncomplicatedCustomTeams.Commands
@@ -21,16 +22,9 @@ public bool Executor(List<string> arguments, ICommandSender _, out string respon
2122
return false;
2223
}
2324

24-
HttpStatusCode Response = Plugin.HttpManager.AddServerOwner(arguments[0]);
25+
HttpStatusCode code = Plugin.HttpManager.AddServerOwner(arguments[0]).GetStatusCode(out response);
2526

26-
response = Response switch
27-
{
28-
HttpStatusCode.OK => $"The request has been accepted!\nNow {arguments[0]} will be flagged as Server Owner!",
29-
HttpStatusCode.Forbidden => "Sorry but your server seems to not be on the public list!\nRetry in three minutes if you think that this is an error!",
30-
HttpStatusCode.BadRequest => "It seems that the Discord user ID is invalid!",
31-
HttpStatusCode.InternalServerError => "The central server is having some issues, please report this message to the Discord as a bug!",
32-
_ => $"The response seems to be invalid.\nRaw format: {Response}",
33-
};
27+
response = $"{code} - {response}";
3428
return true;
3529
}
3630
}

0 commit comments

Comments
 (0)