feat: godot-engine-source-4.3-stable
This commit is contained in:
parent
c59a7dcade
commit
7125d019b5
11149 changed files with 5070401 additions and 0 deletions
|
|
@ -0,0 +1,169 @@
|
|||
using GodotTools.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public class DotNetSolution
|
||||
{
|
||||
private const string _solutionTemplate =
|
||||
@"Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2012
|
||||
{0}
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
{1}
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{2}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
";
|
||||
|
||||
private const string _projectDeclaration =
|
||||
@"Project(""{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}"") = ""{0}"", ""{1}"", ""{{{2}}}""
|
||||
EndProject";
|
||||
|
||||
private const string _solutionPlatformsConfig =
|
||||
@" {0}|Any CPU = {0}|Any CPU";
|
||||
|
||||
private const string _projectPlatformsConfig =
|
||||
@" {{{0}}}.{1}|Any CPU.ActiveCfg = {1}|Any CPU
|
||||
{{{0}}}.{1}|Any CPU.Build.0 = {1}|Any CPU";
|
||||
|
||||
private readonly Dictionary<string, ProjectInfo> _projects = new Dictionary<string, ProjectInfo>();
|
||||
|
||||
public string Name { get; }
|
||||
public string DirectoryPath { get; }
|
||||
|
||||
public class ProjectInfo
|
||||
{
|
||||
public string Guid { get; }
|
||||
public string PathRelativeToSolution { get; }
|
||||
public List<string> Configs { get; }
|
||||
|
||||
public ProjectInfo(string guid, string pathRelativeToSolution, List<string> configs)
|
||||
{
|
||||
Guid = guid;
|
||||
PathRelativeToSolution = pathRelativeToSolution;
|
||||
Configs = configs;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddNewProject(string name, ProjectInfo projectInfo)
|
||||
{
|
||||
_projects[name] = projectInfo;
|
||||
}
|
||||
|
||||
public bool HasProject(string name)
|
||||
{
|
||||
return _projects.ContainsKey(name);
|
||||
}
|
||||
|
||||
public ProjectInfo GetProjectInfo(string name)
|
||||
{
|
||||
return _projects[name];
|
||||
}
|
||||
|
||||
public bool RemoveProject(string name)
|
||||
{
|
||||
return _projects.Remove(name);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
if (!Directory.Exists(DirectoryPath))
|
||||
throw new FileNotFoundException("The solution directory does not exist.");
|
||||
|
||||
string projectsDecl = string.Empty;
|
||||
string slnPlatformsCfg = string.Empty;
|
||||
string projPlatformsCfg = string.Empty;
|
||||
|
||||
bool isFirstProject = true;
|
||||
|
||||
foreach (var pair in _projects)
|
||||
{
|
||||
string name = pair.Key;
|
||||
ProjectInfo projectInfo = pair.Value;
|
||||
|
||||
if (!isFirstProject)
|
||||
projectsDecl += "\n";
|
||||
|
||||
projectsDecl += string.Format(CultureInfo.InvariantCulture, _projectDeclaration,
|
||||
name, projectInfo.PathRelativeToSolution.Replace("/", "\\", StringComparison.Ordinal), projectInfo.Guid);
|
||||
|
||||
for (int i = 0; i < projectInfo.Configs.Count; i++)
|
||||
{
|
||||
string config = projectInfo.Configs[i];
|
||||
|
||||
if (i != 0 || !isFirstProject)
|
||||
{
|
||||
slnPlatformsCfg += "\n";
|
||||
projPlatformsCfg += "\n";
|
||||
}
|
||||
|
||||
slnPlatformsCfg += string.Format(CultureInfo.InvariantCulture, _solutionPlatformsConfig, config);
|
||||
projPlatformsCfg += string.Format(CultureInfo.InvariantCulture, _projectPlatformsConfig, projectInfo.Guid, config);
|
||||
}
|
||||
|
||||
isFirstProject = false;
|
||||
}
|
||||
|
||||
string solutionPath = Path.Combine(DirectoryPath, Name + ".sln");
|
||||
string content = string.Format(CultureInfo.InvariantCulture, _solutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg);
|
||||
|
||||
File.WriteAllText(solutionPath, content, Encoding.UTF8); // UTF-8 with BOM
|
||||
}
|
||||
|
||||
public DotNetSolution(string name, string directoryPath)
|
||||
{
|
||||
Name = name;
|
||||
DirectoryPath = directoryPath.IsAbsolutePath() ? directoryPath : Path.GetFullPath(directoryPath);
|
||||
}
|
||||
|
||||
public static void MigrateFromOldConfigNames(string slnPath)
|
||||
{
|
||||
if (!File.Exists(slnPath))
|
||||
return;
|
||||
|
||||
string input = File.ReadAllText(slnPath);
|
||||
|
||||
if (!Regex.IsMatch(input, Regex.Escape("Tools|Any CPU")))
|
||||
return;
|
||||
|
||||
// This method renames old configurations in solutions to the new ones.
|
||||
//
|
||||
// This is the order configs appear in the solution and what we want to rename them to:
|
||||
// Debug|Any CPU = Debug|Any CPU -> ExportDebug|Any CPU = ExportDebug|Any CPU
|
||||
// Tools|Any CPU = Tools|Any CPU -> Debug|Any CPU = Debug|Any CPU
|
||||
//
|
||||
// But we want to move Tools (now Debug) to the top, so it's easier to rename like this:
|
||||
// Debug|Any CPU = Debug|Any CPU -> Debug|Any CPU = Debug|Any CPU
|
||||
// Release|Any CPU = Release|Any CPU -> ExportDebug|Any CPU = ExportDebug|Any CPU
|
||||
// Tools|Any CPU = Tools|Any CPU -> ExportRelease|Any CPU = ExportRelease|Any CPU
|
||||
|
||||
var dict = new Dictionary<string, string>
|
||||
{
|
||||
{"Debug|Any CPU", "Debug|Any CPU"},
|
||||
{"Release|Any CPU", "ExportDebug|Any CPU"},
|
||||
{"Tools|Any CPU", "ExportRelease|Any CPU"}
|
||||
};
|
||||
|
||||
var regex = new Regex(string.Join("|", dict.Keys.Select(Regex.Escape)));
|
||||
string result = regex.Replace(input, m => dict[m.Value]);
|
||||
|
||||
if (result != input)
|
||||
{
|
||||
// Save a copy of the solution before replacing it
|
||||
FileUtils.SaveBackupCopy(slnPath);
|
||||
|
||||
File.WriteAllText(slnPath, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="15.1.548" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
|
||||
<ProjectReference Include="..\GodotTools.Shared\GodotTools.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public static class IdentifierUtils
|
||||
{
|
||||
public static string SanitizeQualifiedIdentifier(string qualifiedIdentifier, bool allowEmptyIdentifiers)
|
||||
{
|
||||
if (string.IsNullOrEmpty(qualifiedIdentifier))
|
||||
throw new ArgumentException($"{nameof(qualifiedIdentifier)} cannot be empty", nameof(qualifiedIdentifier));
|
||||
|
||||
string[] identifiers = qualifiedIdentifier.Split('.');
|
||||
|
||||
for (int i = 0; i < identifiers.Length; i++)
|
||||
{
|
||||
identifiers[i] = SanitizeIdentifier(identifiers[i], allowEmpty: allowEmptyIdentifiers);
|
||||
}
|
||||
|
||||
return string.Join(".", identifiers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skips invalid identifier characters including decimal digit numbers at the start of the identifier.
|
||||
/// </summary>
|
||||
private static void SkipInvalidCharacters(string source, int startIndex, StringBuilder outputBuilder)
|
||||
{
|
||||
for (int i = startIndex; i < source.Length; i++)
|
||||
{
|
||||
char @char = source[i];
|
||||
|
||||
switch (char.GetUnicodeCategory(@char))
|
||||
{
|
||||
case UnicodeCategory.UppercaseLetter:
|
||||
case UnicodeCategory.LowercaseLetter:
|
||||
case UnicodeCategory.TitlecaseLetter:
|
||||
case UnicodeCategory.ModifierLetter:
|
||||
case UnicodeCategory.LetterNumber:
|
||||
case UnicodeCategory.OtherLetter:
|
||||
outputBuilder.Append(@char);
|
||||
break;
|
||||
case UnicodeCategory.NonSpacingMark:
|
||||
case UnicodeCategory.SpacingCombiningMark:
|
||||
case UnicodeCategory.ConnectorPunctuation:
|
||||
case UnicodeCategory.DecimalDigitNumber:
|
||||
// Identifiers may start with underscore
|
||||
if (outputBuilder.Length > startIndex || @char == '_')
|
||||
outputBuilder.Append(@char);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string SanitizeIdentifier(string identifier, bool allowEmpty)
|
||||
{
|
||||
if (string.IsNullOrEmpty(identifier))
|
||||
{
|
||||
if (allowEmpty)
|
||||
return "Empty"; // Default value for empty identifiers
|
||||
|
||||
throw new ArgumentException($"{nameof(identifier)} cannot be empty if {nameof(allowEmpty)} is false", nameof(identifier));
|
||||
}
|
||||
|
||||
if (identifier.Length > 511)
|
||||
identifier = identifier.Substring(0, 511);
|
||||
|
||||
var identifierBuilder = new StringBuilder();
|
||||
int startIndex = 0;
|
||||
|
||||
if (identifier[0] == '@')
|
||||
{
|
||||
identifierBuilder.Append('@');
|
||||
startIndex += 1;
|
||||
}
|
||||
|
||||
SkipInvalidCharacters(identifier, startIndex, identifierBuilder);
|
||||
|
||||
if (identifierBuilder.Length == startIndex)
|
||||
{
|
||||
// All characters were invalid so now it's empty. Fill it with something.
|
||||
identifierBuilder.Append("Empty");
|
||||
}
|
||||
|
||||
identifier = identifierBuilder.ToString();
|
||||
|
||||
if (identifier[0] != '@' && IsKeyword(identifier, anyDoubleUnderscore: true))
|
||||
identifier = '@' + identifier;
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
||||
private static bool IsKeyword(string value, bool anyDoubleUnderscore)
|
||||
{
|
||||
// Identifiers that start with double underscore are meant to be used for reserved keywords.
|
||||
// Only existing keywords are enforced, but it may be useful to forbid any identifier
|
||||
// that begins with double underscore to prevent issues with future C# versions.
|
||||
if (anyDoubleUnderscore)
|
||||
{
|
||||
if (value.Length > 2 && value[0] == '_' && value[1] == '_' && value[2] != '_')
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_doubleUnderscoreKeywords.Contains(value))
|
||||
return true;
|
||||
}
|
||||
|
||||
return _keywords.Contains(value);
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> _doubleUnderscoreKeywords = new HashSet<string>
|
||||
{
|
||||
"__arglist",
|
||||
"__makeref",
|
||||
"__reftype",
|
||||
"__refvalue",
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> _keywords = new HashSet<string>
|
||||
{
|
||||
"as",
|
||||
"do",
|
||||
"if",
|
||||
"in",
|
||||
"is",
|
||||
"for",
|
||||
"int",
|
||||
"new",
|
||||
"out",
|
||||
"ref",
|
||||
"try",
|
||||
"base",
|
||||
"bool",
|
||||
"byte",
|
||||
"case",
|
||||
"char",
|
||||
"else",
|
||||
"enum",
|
||||
"goto",
|
||||
"lock",
|
||||
"long",
|
||||
"null",
|
||||
"this",
|
||||
"true",
|
||||
"uint",
|
||||
"void",
|
||||
"break",
|
||||
"catch",
|
||||
"class",
|
||||
"const",
|
||||
"event",
|
||||
"false",
|
||||
"fixed",
|
||||
"float",
|
||||
"sbyte",
|
||||
"short",
|
||||
"throw",
|
||||
"ulong",
|
||||
"using",
|
||||
"where",
|
||||
"while",
|
||||
"yield",
|
||||
"double",
|
||||
"extern",
|
||||
"object",
|
||||
"params",
|
||||
"public",
|
||||
"return",
|
||||
"sealed",
|
||||
"sizeof",
|
||||
"static",
|
||||
"string",
|
||||
"struct",
|
||||
"switch",
|
||||
"typeof",
|
||||
"unsafe",
|
||||
"ushort",
|
||||
"checked",
|
||||
"decimal",
|
||||
"default",
|
||||
"finally",
|
||||
"foreach",
|
||||
"partial",
|
||||
"private",
|
||||
"virtual",
|
||||
"abstract",
|
||||
"continue",
|
||||
"delegate",
|
||||
"explicit",
|
||||
"implicit",
|
||||
"internal",
|
||||
"operator",
|
||||
"override",
|
||||
"readonly",
|
||||
"volatile",
|
||||
"interface",
|
||||
"namespace",
|
||||
"protected",
|
||||
"unchecked",
|
||||
"stackalloc",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.Build.Construction;
|
||||
using Microsoft.Build.Evaluation;
|
||||
using GodotTools.Shared;
|
||||
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public static class ProjectGenerator
|
||||
{
|
||||
public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GeneratedGodotNupkgsVersions.GodotNETSdk}";
|
||||
|
||||
public static ProjectRootElement GenGameProject(string name)
|
||||
{
|
||||
if (name.Length == 0)
|
||||
throw new ArgumentException("Project name is empty.", nameof(name));
|
||||
|
||||
var root = ProjectRootElement.Create(NewProjectFileOptions.None);
|
||||
|
||||
root.Sdk = GodotSdkAttrValue;
|
||||
|
||||
var mainGroup = root.AddPropertyGroup();
|
||||
mainGroup.AddProperty("TargetFramework", "net6.0");
|
||||
|
||||
var net7 = mainGroup.AddProperty("TargetFramework", "net7.0");
|
||||
net7.Condition = " '$(GodotTargetPlatform)' == 'android' ";
|
||||
|
||||
var net8 = mainGroup.AddProperty("TargetFramework", "net8.0");
|
||||
net8.Condition = " '$(GodotTargetPlatform)' == 'ios' ";
|
||||
|
||||
mainGroup.AddProperty("EnableDynamicLoading", "true");
|
||||
|
||||
string sanitizedName = IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true);
|
||||
|
||||
// If the name is not a valid namespace, manually set RootNamespace to a sanitized one.
|
||||
if (sanitizedName != name)
|
||||
mainGroup.AddProperty("RootNamespace", sanitizedName);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
public static string GenAndSaveGameProject(string dir, string name)
|
||||
{
|
||||
if (name.Length == 0)
|
||||
throw new ArgumentException("Project name is empty.", nameof(name));
|
||||
|
||||
string path = Path.Combine(dir, name + ".csproj");
|
||||
|
||||
var root = GenGameProject(name);
|
||||
|
||||
// Save (without BOM)
|
||||
root.Save(path, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
|
||||
|
||||
return Guid.NewGuid().ToString().ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Build.Construction;
|
||||
using Microsoft.Build.Locator;
|
||||
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public sealed class MSBuildProject
|
||||
{
|
||||
internal ProjectRootElement Root { get; set; }
|
||||
|
||||
public bool HasUnsavedChanges { get; set; }
|
||||
|
||||
public void Save() => Root.Save();
|
||||
|
||||
public MSBuildProject(ProjectRootElement root)
|
||||
{
|
||||
Root = root;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProjectUtils
|
||||
{
|
||||
public static void MSBuildLocatorRegisterLatest(out Version version, out string path)
|
||||
{
|
||||
var instance = MSBuildLocator.QueryVisualStudioInstances()
|
||||
.OrderByDescending(x => x.Version)
|
||||
.First();
|
||||
MSBuildLocator.RegisterInstance(instance);
|
||||
version = instance.Version;
|
||||
path = instance.MSBuildPath;
|
||||
}
|
||||
|
||||
public static void MSBuildLocatorRegisterMSBuildPath(string msbuildPath)
|
||||
=> MSBuildLocator.RegisterMSBuildPath(msbuildPath);
|
||||
|
||||
public static MSBuildProject? Open(string path)
|
||||
{
|
||||
var root = ProjectRootElement.Open(path);
|
||||
return root != null ? new MSBuildProject(root) : null;
|
||||
}
|
||||
|
||||
public static void MigrateToProjectSdksStyle(MSBuildProject project, string projectName)
|
||||
{
|
||||
var origRoot = project.Root;
|
||||
|
||||
if (!string.IsNullOrEmpty(origRoot.Sdk))
|
||||
return;
|
||||
|
||||
project.Root = ProjectGenerator.GenGameProject(projectName);
|
||||
project.Root.FullPath = origRoot.FullPath;
|
||||
project.HasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
public static void EnsureGodotSdkIsUpToDate(MSBuildProject project)
|
||||
{
|
||||
var root = project.Root;
|
||||
string godotSdkAttrValue = ProjectGenerator.GodotSdkAttrValue;
|
||||
|
||||
if (!string.IsNullOrEmpty(root.Sdk) &&
|
||||
root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase))
|
||||
return;
|
||||
|
||||
root.Sdk = godotSdkAttrValue;
|
||||
project.HasUnsavedChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue