Skip to content

Commit 055c75a

Browse files
authored
Merge branch 'CnCNet:develop' into develop
2 parents 93693b0 + bad593d commit 055c75a

116 files changed

Lines changed: 5971 additions & 1873 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ jobs:
1313
- uses: actions/checkout@v4
1414
with:
1515
fetch-depth: 0
16+
submodules: recursive
1617

1718
- name: Setup .NET SDK
18-
uses: actions/setup-dotnet@v4
19+
uses: actions/setup-dotnet@v5
1920
with:
2021
global-json-file: ./global.json
2122

.github/workflows/release-build.yml

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ on:
44
release:
55
types: [published]
66

7+
permissions:
8+
contents: write
9+
710
jobs:
811
build:
912
runs-on: windows-2022
@@ -13,24 +16,34 @@ jobs:
1316
uses: actions/checkout@v4
1417
with:
1518
fetch-depth: 0
19+
submodules: recursive
1620

1721
- name: Setup .NET SDK
18-
uses: actions/setup-dotnet@v4
22+
uses: actions/setup-dotnet@v5
1923
with:
2024
global-json-file: ./global.json
2125

22-
- name: Build
23-
run: ./Scripts/build.ps1
24-
shell: pwsh
26+
- name: Install GitVersion
27+
uses: gittools/actions/gitversion/setup@v0
28+
with:
29+
versionSpec: '5.x'
30+
31+
- name: Determine Version
32+
uses: gittools/actions/gitversion/execute@v0
2533

2634
- name: Development Build Check
35+
if: "!github.event.release.prerelease"
2736
shell: pwsh
2837
run: |
2938
if ($env:GitVersion_CommitsSinceVersionSource -ne "0") {
3039
Write-Output "::error:: This is a development build and should not be released. Did you forget to create a new tag for the release?"
3140
exit 1
3241
}
3342
43+
- name: Build
44+
run: ./Scripts/build.ps1
45+
shell: pwsh
46+
3447
- name: Zip Artifact
3548
run: 7z a -t7z -mx=9 -m0=lzma2 -ms=on -r -- ${{ format('xna-cncnet-client-{0}.7z', env.GitVersion_SemVer) }} ./Compiled/*
3649
shell: pwsh

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "Rampastring.XNAUI"]
2+
path = Rampastring.XNAUI
3+
url = https://github.com/CnCNet/Rampastring.XNAUI.git

ClientCore/ClientConfiguration.cs

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,18 @@ public void RefreshSettings()
9090

9191
#region Client settings
9292

93-
public string MainMenuMusicName => SafePath.CombineFilePath(DTACnCNetClient_ini.GetStringValue(GENERAL, "MainMenuTheme", "mainmenu"));
93+
private string _mainMenuMusicName = null;
94+
public string MainMenuMusicName => _mainMenuMusicName ??= GetMainMenuMusicName();
95+
private string GetMainMenuMusicName()
96+
{
97+
string raw = DTACnCNetClient_ini.GetStringValue(GENERAL, "MainMenuTheme", "mainmenu");
98+
string[] parts = raw.SplitWithCleanup();
99+
string chosen = parts.Length > 0
100+
? parts[new Random().Next(parts.Length)]
101+
: "mainmenu";
102+
103+
return SafePath.CombineFilePath(chosen);
104+
}
94105

95106
public float DefaultAlphaRate => DTACnCNetClient_ini.GetSingleValue(GENERAL, "AlphaRate", 0.005f);
96107

@@ -258,13 +269,17 @@ public void RefreshSettings()
258269

259270
public bool UseBuiltStatistic => clientDefinitionsIni.GetBooleanValue(SETTINGS, "UseBuiltStatistic", false);
260271

272+
public string WindowedModeKey => clientDefinitionsIni.GetStringValue(SETTINGS, "WindowedModeKey", "Video.Windowed");
273+
261274
public bool CopyResolutionDependentLanguageDLL => clientDefinitionsIni.GetBooleanValue(SETTINGS, "CopyResolutionDependentLanguageDLL", true);
262275

263276
public string StatisticsLogFileName => clientDefinitionsIni.GetStringValue(SETTINGS, "StatisticsLogFileName", "DTA.LOG");
264277

265278
public string[] TrustedDomains => clientDefinitionsIni.GetStringListValue(SETTINGS, "TrustedDomains", string.Empty);
266279

267-
public string[] AlwaysTrustedDomains = {"cncnet.org", "gamesurge.net", "dronebl.org", "discord.com", "discord.gg", "youtube.com", "youtu.be"};
280+
public string[] AlwaysTrustedDomains = { "cncnet.org", "gamesurge.net", "dronebl.org", "discord.com", "discord.gg", "youtube.com", "youtu.be" };
281+
282+
public bool ShowGameIconInGameList => clientDefinitionsIni.GetBooleanValue(SETTINGS, "ShowGameIconInGameList", true);
268283

269284
public (string Name, string Path) GetThemeInfoFromIndex(int themeIndex) => clientDefinitionsIni.GetStringValue("Themes", themeIndex.ToString(), ",").Split(',').AsTuple2();
270285

@@ -369,9 +384,13 @@ private List<TranslationGameFile> ParseTranslationGameFiles()
369384
public bool InactiveHostKickEnabled => InactiveHostWarningMessageSeconds > 0 && InactiveHostKickSeconds > 0;
370385

371386
public string SkillLevelOptions => clientDefinitionsIni.GetStringValue(SETTINGS, "SkillLevelOptions", "Any,Beginner,Intermediate,Pro");
372-
387+
373388
public int DefaultSkillLevelIndex => clientDefinitionsIni.GetIntValue(SETTINGS, "DefaultSkillLevelIndex", 0);
374-
389+
390+
public bool CampaignTagSelectorEnabled => clientDefinitionsIni.GetBooleanValue(SETTINGS, "CampaignTagSelectorEnabled", false);
391+
392+
public bool ReturnToMainMenuOnMissionLaunch => clientDefinitionsIni.GetBooleanValue(SETTINGS, "ReturnToMainMenuOnMissionLaunch", true);
393+
375394
public string GetGameExecutableName()
376395
{
377396
string[] exeNames = clientDefinitionsIni.GetStringListValue(SETTINGS, "GameExecutableNames", "Game.exe");
@@ -381,8 +400,15 @@ public string GetGameExecutableName()
381400

382401
public string GameLauncherExecutableName => clientDefinitionsIni.GetStringValue(SETTINGS, "GameLauncherExecutableName", string.Empty);
383402

403+
public string[] GetCompatibilityCheckExecutables()
404+
{
405+
string[] exeNames = clientDefinitionsIni.GetStringListValue(SETTINGS, "CompatibilityCheckExecutables", string.Empty);
406+
407+
return exeNames;
408+
}
409+
384410
public bool SaveSkirmishGameOptions => clientDefinitionsIni.GetBooleanValue(SETTINGS, "SaveSkirmishGameOptions", false);
385-
411+
386412
public bool SaveCampaignGameOptions => clientDefinitionsIni.GetBooleanValue(SETTINGS, "SaveCampaignGameOptions", false);
387413

388414
public bool CreateSavedGamesDirectory => clientDefinitionsIni.GetBooleanValue(SETTINGS, "CreateSavedGamesDirectory", false);
@@ -420,7 +446,7 @@ public string GetGameExecutableName()
420446
/// The main map file extension that is read by the client.
421447
/// </summary>
422448
public string MapFileExtension => clientDefinitionsIni.GetStringValue(SETTINGS, "MapFileExtension", "map");
423-
449+
424450
/// <summary>
425451
/// This tells the client which supplemental map files are ok to copy over during "spawnmap.ini" file creation.
426452
/// IE, if "BIN" is listed, then the client will look for and copy the file "map_a.bin"
@@ -505,6 +531,43 @@ public List<string> GetIRCServers()
505531

506532
public bool DiscordIntegrationGloballyDisabled => string.IsNullOrWhiteSpace(DiscordAppId) || DisableDiscordIntegration;
507533

534+
public string CustomMissionPath => clientDefinitionsIni.GetStringValue(SETTINGS, "CustomMissionPath", "Maps/CustomMissions");
535+
536+
public List<(string extension, string copyAs)> GetCustomMissionSupplementFiles()
537+
{
538+
List<(string extension, string copyAs)> files = new();
539+
Dictionary<string, int> extensionToIndex = new(StringComparer.OrdinalIgnoreCase);
540+
541+
int index = 0;
542+
while (true)
543+
{
544+
string extensionKey = $"CustomMissionSupplementFile{index}Extension";
545+
string copyAsKey = $"CustomMissionSupplementFile{index}CopyAs";
546+
547+
string extension = clientDefinitionsIni.GetStringValue(SETTINGS, extensionKey, null)?.Trim();
548+
549+
// Stop iteration if the extension key is missing
550+
if (string.IsNullOrWhiteSpace(extension))
551+
break;
552+
553+
string copyAs = clientDefinitionsIni.GetStringValue(SETTINGS, copyAsKey, null);
554+
555+
// Validate that copyAs is not empty
556+
if (string.IsNullOrWhiteSpace(copyAs))
557+
throw new ClientConfigurationException($"Configuration key '{copyAsKey}' is required when '{extensionKey}' is present for supplement file {index}.");
558+
559+
// Validate that extension is unique
560+
if (extensionToIndex.TryGetValue(extension, out int firstIndex))
561+
throw new ClientConfigurationException($"Duplicate extension '{extension}' found in supplement files. Extension is used in both file {firstIndex} and file {index}.");
562+
563+
extensionToIndex.Add(extension, index);
564+
files.Add((extension, copyAs));
565+
index++;
566+
}
567+
568+
return files;
569+
}
570+
508571
public OSVersion GetOperatingSystemVersion()
509572
{
510573
#if NETFRAMEWORK
@@ -567,4 +630,4 @@ public ClientConfigurationException(string message) : base(message)
567630
{
568631
}
569632
}
570-
}
633+
}

ClientCore/ClientCore.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
<Description>CnCNet Client Core Library</Description>
44
</PropertyGroup>
55
<ItemGroup>
6-
<PackageReference Include="Rampastring.Tools" />
76
<PackageReference Include="System.Text.Encoding.CodePages" />
87
<PackageReference Include="Ude.NetStandard" />
98
</ItemGroup>
9+
<ItemGroup>
10+
<ProjectReference Include="..\Rampastring.XNAUI\Rampastring.Tools\Rampastring.Tools.csproj" />
11+
</ItemGroup>
1012
</Project>
Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,79 @@
1-
using System.Collections.Generic;
1+
#nullable enable
2+
3+
using System;
4+
using System.Collections.Generic;
25
using System.Linq;
3-
using System.Runtime.CompilerServices;
46

57
using Rampastring.Tools;
68

79
namespace ClientCore.Extensions
810
{
911
public static class IniFileExtensions
1012
{
11-
// Clone() method is not officially available now. https://github.com/Rampastring/Rampastring.Tools/issues/12
12-
public static IniFile Clone(this IniFile oldIniFile)
13+
extension(IniFile iniFile)
1314
{
14-
var newIni = new IniFile();
15-
foreach (string sectionName in oldIniFile.GetSections())
15+
// Clone() method is not officially available now. https://github.com/Rampastring/Rampastring.Tools/issues/12
16+
public IniFile Clone()
17+
{
18+
var newIni = new IniFile();
19+
foreach (string sectionName in iniFile.GetSections())
20+
{
21+
IniSection oldSection = iniFile.GetSection(sectionName);
22+
newIni.AddSection(oldSection.Clone());
23+
}
24+
25+
return newIni;
26+
}
27+
28+
public IniSection GetOrAddSection(string sectionName)
1629
{
17-
IniSection oldSection = oldIniFile.GetSection(sectionName);
18-
newIni.AddSection(oldSection.Clone());
30+
var section = iniFile.GetSection(sectionName);
31+
if (section != null)
32+
return section;
33+
34+
section = new IniSection(sectionName);
35+
iniFile.AddSection(section);
36+
return section;
1937
}
2038

21-
return newIni;
39+
public string[] GetStringListValue(string section, string key, string defaultValue, char[]? separators = null)
40+
=> (iniFile.GetSection(section)?.GetStringValue(key, defaultValue) ?? defaultValue)
41+
.SplitWithCleanup(separators);
42+
2243
}
2344

24-
public static IniSection Clone(this IniSection oldSection)
45+
extension(IniSection iniSection)
2546
{
26-
IniSection newSection = new(oldSection.SectionName);
27-
28-
foreach ((var key, var value) in oldSection.Keys)
47+
public IniSection Clone()
2948
{
30-
newSection.AddKey(key, value);
49+
IniSection newSection = new(iniSection.SectionName);
50+
51+
foreach ((var key, var value) in iniSection.Keys)
52+
{
53+
newSection.AddKey(key, value);
54+
}
55+
56+
return newSection;
3157
}
3258

33-
return newSection;
34-
}
59+
public void RemoveAllKeys()
60+
{
61+
var keys = new List<KeyValuePair<string, string>>(iniSection.Keys);
62+
foreach (KeyValuePair<string, string> iniSectionKey in keys)
63+
iniSection.RemoveKey(iniSectionKey.Key);
64+
}
65+
66+
public string? GetStringValueOrNull(string key) =>
67+
iniSection.KeyExists(key) ? iniSection.GetStringValue(key, string.Empty) : null;
3568

36-
public static IniSection GetOrAddSection(this IniFile iniFile, string sectionName)
37-
{
38-
var section = iniFile.GetSection(sectionName);
39-
if (section != null)
40-
return section;
69+
public int? GetIntValueOrNull(string key) =>
70+
iniSection.KeyExists(key) ? iniSection.GetIntValue(key, 0) : null;
4171

42-
section = new IniSection(sectionName);
43-
iniFile.AddSection(section);
44-
return section;
45-
}
72+
public bool? GetBooleanValueOrNull(string key) =>
73+
iniSection.KeyExists(key) ? iniSection.GetBooleanValue(key, false) : null;
4674

47-
public static void RemoveAllKeys(this IniSection iniSection)
48-
{
49-
var keys = new List<KeyValuePair<string, string>>(iniSection.Keys);
50-
foreach (KeyValuePair<string, string> iniSectionKey in keys)
51-
iniSection.RemoveKey(iniSectionKey.Key);
75+
public List<T>? GetListValueOrNull<T>(string key, char separator, Func<string, T> converter) =>
76+
iniSection.KeyExists(key) ? iniSection.GetListValue<T>(key, separator, converter) : null;
5277
}
53-
54-
public static string[] GetStringListValue(this IniFile iniFile, string section, string key, string defaultValue, char[] separators = null)
55-
=> (iniFile.GetSection(section)?.GetStringValue(key, defaultValue) ?? defaultValue)
56-
.SplitWithCleanup();
5778
}
5879
}

ClientCore/ProgramConstants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public static class ProgramConstants
2626

2727
public const string QRES_EXECUTABLE = "qres.dat";
2828

29-
public const string CNCNET_PROTOCOL_REVISION = "R13";
29+
public const string CNCNET_PROTOCOL_REVISION = "R14";
3030
public const string LAN_PROTOCOL_REVISION = "RL8";
3131
public const int LAN_PORT = 1234;
3232
public const int LAN_INGAME_PORT = 1234;

0 commit comments

Comments
 (0)