mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-11 01:37:24 +01:00
Merge remote-tracking branch 'origin/master' into api14-rollup
This commit is contained in:
commit
0072f49fe8
13 changed files with 254 additions and 98 deletions
209
.github/generate_changelog.py
vendored
209
.github/generate_changelog.py
vendored
|
|
@ -8,7 +8,8 @@ import re
|
|||
import sys
|
||||
import json
|
||||
import argparse
|
||||
from typing import List, Tuple, Optional
|
||||
import os
|
||||
from typing import List, Tuple, Optional, Dict, Any
|
||||
|
||||
|
||||
def run_git_command(args: List[str]) -> str:
|
||||
|
|
@ -30,14 +31,14 @@ def get_last_two_tags() -> Tuple[str, str]:
|
|||
"""Get the latest two git tags."""
|
||||
tags = run_git_command(["tag", "--sort=-version:refname"])
|
||||
tag_list = [t for t in tags.split("\n") if t]
|
||||
|
||||
|
||||
# Filter out old tags that start with 'v' (old versioning scheme)
|
||||
tag_list = [t for t in tag_list if not t.startswith('v')]
|
||||
|
||||
if len(tag_list) < 2:
|
||||
print("Error: Need at least 2 tags in the repository", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
return tag_list[0], tag_list[1]
|
||||
|
||||
|
||||
|
|
@ -55,58 +56,144 @@ def get_submodule_commit(submodule_path: str, tag: str) -> Optional[str]:
|
|||
return None
|
||||
|
||||
|
||||
def get_commits_between_tags(tag1: str, tag2: str) -> List[Tuple[str, str]]:
|
||||
"""Get commits between two tags. Returns list of (message, author) tuples."""
|
||||
def get_repo_info() -> Tuple[str, str]:
|
||||
"""Get repository owner and name from git remote."""
|
||||
try:
|
||||
remote_url = run_git_command(["config", "--get", "remote.origin.url"])
|
||||
|
||||
# Handle both HTTPS and SSH URLs
|
||||
# SSH: git@github.com:owner/repo.git
|
||||
# HTTPS: https://github.com/owner/repo.git
|
||||
match = re.search(r'github\.com[:/](.+?)/(.+?)(?:\.git)?$', remote_url)
|
||||
if match:
|
||||
owner = match.group(1)
|
||||
repo = match.group(2)
|
||||
return owner, repo
|
||||
else:
|
||||
print("Error: Could not parse GitHub repository from remote URL", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except:
|
||||
print("Error: Could not get git remote URL", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_commits_between_tags(tag1: str, tag2: str) -> List[str]:
|
||||
"""Get commit SHAs between two tags."""
|
||||
log_output = run_git_command([
|
||||
"log",
|
||||
f"{tag2}..{tag1}",
|
||||
"--format=%s|%an|%h"
|
||||
"--format=%H"
|
||||
])
|
||||
|
||||
commits = []
|
||||
for line in log_output.split("\n"):
|
||||
if "|" in line:
|
||||
message, author, sha = line.split("|", 2)
|
||||
commits.append((message.strip(), author.strip(), sha.strip()))
|
||||
|
||||
|
||||
commits = [sha.strip() for sha in log_output.split("\n") if sha.strip()]
|
||||
return commits
|
||||
|
||||
|
||||
def filter_commits(commits: List[Tuple[str, str]], ignore_patterns: List[str]) -> List[Tuple[str, str]]:
|
||||
"""Filter out commits matching any of the ignore patterns."""
|
||||
def get_pr_for_commit(commit_sha: str, owner: str, repo: str, token: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get PR information for a commit using GitHub API."""
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
print("Error: requests library is required. Install it with: pip install requests", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
headers = {
|
||||
"Accept": "application/vnd.github+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28"
|
||||
}
|
||||
|
||||
if token:
|
||||
headers["Authorization"] = f"Bearer {token}"
|
||||
|
||||
url = f"https://api.github.com/repos/{owner}/{repo}/commits/{commit_sha}/pulls"
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=headers)
|
||||
response.raise_for_status()
|
||||
prs = response.json()
|
||||
|
||||
if prs and len(prs) > 0:
|
||||
# Return the first PR (most relevant one)
|
||||
pr = prs[0]
|
||||
return {
|
||||
"number": pr["number"],
|
||||
"title": pr["title"],
|
||||
"author": pr["user"]["login"],
|
||||
"url": pr["html_url"]
|
||||
}
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if e.response.status_code == 404:
|
||||
# Commit might not be associated with a PR
|
||||
return None
|
||||
elif e.response.status_code == 403:
|
||||
print("Warning: GitHub API rate limit exceeded. Consider providing a token.", file=sys.stderr)
|
||||
return None
|
||||
else:
|
||||
print(f"Warning: Failed to fetch PR for commit {commit_sha[:7]}: {e}", file=sys.stderr)
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Warning: Error fetching PR for commit {commit_sha[:7]}: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_prs_between_tags(tag1: str, tag2: str, owner: str, repo: str, token: str) -> List[Dict[str, Any]]:
|
||||
"""Get PRs between two tags using GitHub API."""
|
||||
commits = get_commits_between_tags(tag1, tag2)
|
||||
print(f"Found {len(commits)} commits, fetching PR information...")
|
||||
|
||||
prs = []
|
||||
seen_pr_numbers = set()
|
||||
|
||||
for i, commit_sha in enumerate(commits, 1):
|
||||
if i % 10 == 0:
|
||||
print(f"Progress: {i}/{len(commits)} commits processed...")
|
||||
|
||||
pr_info = get_pr_for_commit(commit_sha, owner, repo, token)
|
||||
if pr_info and pr_info["number"] not in seen_pr_numbers:
|
||||
seen_pr_numbers.add(pr_info["number"])
|
||||
prs.append(pr_info)
|
||||
|
||||
return prs
|
||||
|
||||
|
||||
def filter_prs(prs: List[Dict[str, Any]], ignore_patterns: List[str]) -> List[Dict[str, Any]]:
|
||||
"""Filter out PRs matching any of the ignore patterns."""
|
||||
compiled_patterns = [re.compile(pattern) for pattern in ignore_patterns]
|
||||
|
||||
|
||||
filtered = []
|
||||
for message, author, sha in commits:
|
||||
if not any(pattern.search(message) for pattern in compiled_patterns):
|
||||
filtered.append((message, author, sha))
|
||||
|
||||
for pr in prs:
|
||||
if not any(pattern.search(pr["title"]) for pattern in compiled_patterns):
|
||||
filtered.append(pr)
|
||||
|
||||
return filtered
|
||||
|
||||
|
||||
def generate_changelog(version: str, prev_version: str, commits: List[Tuple[str, str]],
|
||||
cs_commit_new: Optional[str], cs_commit_old: Optional[str]) -> str:
|
||||
def generate_changelog(version: str, prev_version: str, prs: List[Dict[str, Any]],
|
||||
cs_commit_new: Optional[str], cs_commit_old: Optional[str],
|
||||
owner: str, repo: str) -> str:
|
||||
"""Generate markdown changelog."""
|
||||
# Calculate statistics
|
||||
commit_count = len(commits)
|
||||
unique_authors = len(set(author for _, author, _ in commits))
|
||||
|
||||
pr_count = len(prs)
|
||||
unique_authors = len(set(pr["author"] for pr in prs))
|
||||
|
||||
changelog = f"# Dalamud Release v{version}\n\n"
|
||||
changelog += f"We just released Dalamud v{version}, which should be available to users within a few minutes. "
|
||||
changelog += f"This release includes **{commit_count} commit{'s' if commit_count != 1 else ''} from {unique_authors} contributor{'s' if unique_authors != 1 else ''}**.\n"
|
||||
changelog += f"[Click here](<https://github.com/goatcorp/Dalamud/compare/{prev_version}...{version}>) to see all Dalamud changes.\n\n"
|
||||
|
||||
changelog += f"This release includes **{pr_count} PR{'s' if pr_count != 1 else ''} from {unique_authors} contributor{'s' if unique_authors != 1 else ''}**.\n"
|
||||
changelog += f"[Click here](<https://github.com/{owner}/{repo}/compare/{prev_version}...{version}>) to see all Dalamud changes.\n\n"
|
||||
|
||||
if cs_commit_new and cs_commit_old and cs_commit_new != cs_commit_old:
|
||||
changelog += f"It ships with an updated **FFXIVClientStructs [`{cs_commit_new[:7]}`](<https://github.com/aers/FFXIVClientStructs/commit/{cs_commit_new}>)**.\n"
|
||||
changelog += f"[Click here](<https://github.com/aers/FFXIVClientStructs/compare/{cs_commit_old}...{cs_commit_new}>) to see all CS changes.\n"
|
||||
elif cs_commit_new:
|
||||
changelog += f"It ships with **FFXIVClientStructs [`{cs_commit_new[:7]}`](<https://github.com/aers/FFXIVClientStructs/commit/{cs_commit_new}>)**.\n"
|
||||
|
||||
|
||||
changelog += "## Dalamud Changes\n\n"
|
||||
|
||||
for message, author, sha in commits:
|
||||
changelog += f"* {message} (by **{author}** as [`{sha}`](<https://github.com/goatcorp/Dalamud/commit/{sha}>))\n"
|
||||
|
||||
|
||||
for pr in prs:
|
||||
changelog += f"* {pr['title']} ([#**{pr['number']}**](<{pr['url']}>) by **{pr['author']}**)\n"
|
||||
|
||||
return changelog
|
||||
|
||||
|
||||
|
|
@ -117,9 +204,9 @@ def post_to_discord(webhook_url: str, content: str, version: str) -> None:
|
|||
except ImportError:
|
||||
print("Error: requests library is required. Install it with: pip install requests", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
filename = f"changelog-v{version}.md"
|
||||
|
||||
|
||||
# Prepare the payload
|
||||
data = {
|
||||
"content": f"Dalamud v{version} has been released!",
|
||||
|
|
@ -130,13 +217,13 @@ def post_to_discord(webhook_url: str, content: str, version: str) -> None:
|
|||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# Prepare the files
|
||||
files = {
|
||||
"payload_json": (None, json.dumps(data)),
|
||||
"files[0]": (filename, content.encode('utf-8'), 'text/markdown')
|
||||
}
|
||||
|
||||
|
||||
try:
|
||||
result = requests.post(webhook_url, files=files)
|
||||
result.raise_for_status()
|
||||
|
|
@ -158,54 +245,64 @@ def main():
|
|||
required=True,
|
||||
help="Discord webhook URL"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--github-token",
|
||||
default=os.environ.get("GITHUB_TOKEN"),
|
||||
help="GitHub API token (or set GITHUB_TOKEN env var). Increases rate limit."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ignore",
|
||||
action="append",
|
||||
default=[],
|
||||
help="Regex patterns to ignore commits (can be specified multiple times)"
|
||||
help="Regex patterns to ignore PRs (can be specified multiple times)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--submodule-path",
|
||||
default="lib/FFXIVClientStructs",
|
||||
help="Path to the FFXIVClientStructs submodule (default: lib/FFXIVClientStructs)"
|
||||
)
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
# Get repository info
|
||||
owner, repo = get_repo_info()
|
||||
print(f"Repository: {owner}/{repo}")
|
||||
|
||||
# Get the last two tags
|
||||
latest_tag, previous_tag = get_last_two_tags()
|
||||
print(f"Generating changelog between {previous_tag} and {latest_tag}")
|
||||
|
||||
|
||||
# Get submodule commits at both tags
|
||||
cs_commit_new = get_submodule_commit(args.submodule_path, latest_tag)
|
||||
cs_commit_old = get_submodule_commit(args.submodule_path, previous_tag)
|
||||
|
||||
|
||||
if cs_commit_new:
|
||||
print(f"FFXIVClientStructs commit (new): {cs_commit_new[:7]}")
|
||||
if cs_commit_old:
|
||||
print(f"FFXIVClientStructs commit (old): {cs_commit_old[:7]}")
|
||||
|
||||
# Get commits between tags
|
||||
commits = get_commits_between_tags(latest_tag, previous_tag)
|
||||
print(f"Found {len(commits)} commits")
|
||||
|
||||
# Filter commits
|
||||
filtered_commits = filter_commits(commits, args.ignore)
|
||||
print(f"After filtering: {len(filtered_commits)} commits")
|
||||
|
||||
|
||||
# Get PRs between tags
|
||||
prs = get_prs_between_tags(latest_tag, previous_tag, owner, repo, args.github_token)
|
||||
prs.reverse()
|
||||
print(f"Found {len(prs)} PRs")
|
||||
|
||||
# Filter PRs
|
||||
filtered_prs = filter_prs(prs, args.ignore)
|
||||
print(f"After filtering: {len(filtered_prs)} PRs")
|
||||
|
||||
# Generate changelog
|
||||
changelog = generate_changelog(latest_tag, previous_tag, filtered_commits,
|
||||
cs_commit_new, cs_commit_old)
|
||||
|
||||
changelog = generate_changelog(latest_tag, previous_tag, filtered_prs,
|
||||
cs_commit_new, cs_commit_old, owner, repo)
|
||||
|
||||
print("\n" + "="*50)
|
||||
print("Generated Changelog:")
|
||||
print("="*50)
|
||||
print(changelog)
|
||||
print("="*50 + "\n")
|
||||
|
||||
|
||||
# Post to Discord
|
||||
post_to_discord(args.webhook_url, changelog, latest_tag)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
|
|
|||
24
.github/workflows/generate-changelog.yml
vendored
24
.github/workflows/generate-changelog.yml
vendored
|
|
@ -6,41 +6,43 @@ on:
|
|||
tags:
|
||||
- '*'
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
generate-changelog:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history and tags
|
||||
submodules: true # Fetch submodules
|
||||
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.14'
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install requests
|
||||
|
||||
|
||||
- name: Generate and post changelog
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GIT_TERMINAL_PROMPT: 0
|
||||
run: |
|
||||
python .github/generate_changelog.py \
|
||||
--webhook-url "${{ secrets.DISCORD_CHANGELOG_WEBHOOK_URL }}" \
|
||||
--ignore "^Merge" \
|
||||
--ignore "^build:" \
|
||||
--ignore "^docs:"
|
||||
env:
|
||||
GIT_TERMINAL_PROMPT: 0
|
||||
|
||||
--ignore "Update ClientStructs" \
|
||||
--ignore "^build:"
|
||||
|
||||
- name: Upload changelog as artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: changelog
|
||||
path: changelog-*.md
|
||||
if-no-files-found: ignore
|
||||
if-no-files-found: ignore
|
||||
|
|
|
|||
|
|
@ -454,3 +454,9 @@ void veh::raise_external_event(const std::wstring& info)
|
|||
wcsncpy_s(g_external_event_info, info.c_str(), info_size);
|
||||
RaiseException(CUSTOM_EXCEPTION_EXTERNAL_EVENT, 0, 0, nullptr);
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void BootVehRaiseExternalEventW(LPCWSTR info)
|
||||
{
|
||||
const std::wstring info_wstr(info);
|
||||
veh::raise_external_event(info_wstr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<PropertyGroup Label="Feature">
|
||||
<Description>XIV Launcher addon framework</Description>
|
||||
<DalamudVersion>13.0.0.12</DalamudVersion>
|
||||
<DalamudVersion>13.0.0.13</DalamudVersion>
|
||||
<AssemblyVersion>$(DalamudVersion)</AssemblyVersion>
|
||||
<Version>$(DalamudVersion)</Version>
|
||||
<FileVersion>$(DalamudVersion)</FileVersion>
|
||||
|
|
|
|||
|
|
@ -82,8 +82,10 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager
|
|||
var tsInfo =
|
||||
JsonConvert.DeserializeObject<LauncherTroubleshootingInfo>(
|
||||
dalamud.StartInfo.TroubleshootingPackData);
|
||||
|
||||
// Don't fail for IndexIntegrityResult.Exception, since the check during launch has a very small timeout
|
||||
this.HasModifiedGameDataFiles =
|
||||
tsInfo?.IndexIntegrity is LauncherTroubleshootingInfo.IndexIntegrityResult.Failed or LauncherTroubleshootingInfo.IndexIntegrityResult.Exception;
|
||||
tsInfo?.IndexIntegrity is LauncherTroubleshootingInfo.IndexIntegrityResult.Failed;
|
||||
|
||||
if (this.HasModifiedGameDataFiles)
|
||||
Log.Verbose("Game data integrity check failed!\n{TsData}", dalamud.StartInfo.TroubleshootingPackData);
|
||||
|
|
|
|||
|
|
@ -292,7 +292,6 @@ public sealed class EntryPoint
|
|||
}
|
||||
|
||||
var pluginInfo = string.Empty;
|
||||
var supportText = ", please visit us on Discord for more help";
|
||||
try
|
||||
{
|
||||
var pm = Service<PluginManager>.GetNullable();
|
||||
|
|
@ -300,9 +299,6 @@ public sealed class EntryPoint
|
|||
if (plugin != null)
|
||||
{
|
||||
pluginInfo = $"Plugin that caused this:\n{plugin.Name}\n\nClick \"Yes\" and remove it.\n\n";
|
||||
|
||||
if (plugin.IsThirdParty)
|
||||
supportText = string.Empty;
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
|
@ -310,31 +306,18 @@ public sealed class EntryPoint
|
|||
// ignored
|
||||
}
|
||||
|
||||
const MESSAGEBOX_STYLE flags = MESSAGEBOX_STYLE.MB_YESNO | MESSAGEBOX_STYLE.MB_ICONERROR | MESSAGEBOX_STYLE.MB_SYSTEMMODAL;
|
||||
var result = Windows.Win32.PInvoke.MessageBox(
|
||||
new HWND(Process.GetCurrentProcess().MainWindowHandle),
|
||||
$"An internal error in a Dalamud plugin occurred.\nThe game must close.\n\n{ex.GetType().Name}\n{info}\n\n{pluginInfo}More information has been recorded separately{supportText}.\n\nDo you want to disable all plugins the next time you start the game?",
|
||||
"Dalamud",
|
||||
flags);
|
||||
|
||||
if (result == MESSAGEBOX_RESULT.IDYES)
|
||||
{
|
||||
Log.Information("User chose to disable plugins on next launch...");
|
||||
var config = Service<DalamudConfiguration>.Get();
|
||||
config.PluginSafeMode = true;
|
||||
config.ForceSave();
|
||||
}
|
||||
|
||||
Log.CloseAndFlush();
|
||||
Environment.Exit(-1);
|
||||
|
||||
ErrorHandling.CrashWithContext($"{ex}\n\n{info}\n\n{pluginInfo}");
|
||||
break;
|
||||
default:
|
||||
Log.Fatal("Unhandled SEH object on AppDomain: {Object}", args.ExceptionObject);
|
||||
|
||||
Log.CloseAndFlush();
|
||||
Environment.Exit(-1);
|
||||
break;
|
||||
}
|
||||
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
|
||||
private static void OnUnhandledExceptionStallDebug(object sender, UnhandledExceptionEventArgs args)
|
||||
|
|
|
|||
|
|
@ -161,7 +161,9 @@ internal class SeStringRenderer : IServiceType
|
|||
ImFont* font = null;
|
||||
if (drawParams.Font.HasValue)
|
||||
font = drawParams.Font.Value;
|
||||
if (ThreadSafety.IsMainThread && drawParams.TargetDrawList is null && font is null)
|
||||
|
||||
// API14: Remove commented out code
|
||||
if (ThreadSafety.IsMainThread /* && drawParams.TargetDrawList is null */ && font is null)
|
||||
font = ImGui.GetFont();
|
||||
if (font is null)
|
||||
throw new ArgumentException("Specified font is empty.");
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@ public record struct SeStringDrawParams
|
|||
/// <see cref="ImGui.GetWindowDrawList"/> (the default).</value>
|
||||
/// <remarks>
|
||||
/// If this value is set, <see cref="ImGui.Dummy"/> will not be called, and ImGui ID will be ignored.
|
||||
/// You <b>must</b> specify a valid draw list and a valid font via <see cref="Font"/> if you set this value,
|
||||
/// You <b>must</b> specify a valid draw list, a valid font via <see cref="Font"/> and <see cref="FontSize"/> if you set this value,
|
||||
/// since the renderer will not be able to retrieve them from ImGui context.
|
||||
/// Must be set when drawing off the main thread.
|
||||
/// </remarks>
|
||||
public ImDrawListPtr? TargetDrawList { get; set; }
|
||||
|
||||
|
|
@ -24,16 +25,20 @@ public record struct SeStringDrawParams
|
|||
public SeStringReplacementEntity.GetEntityDelegate? GetEntity { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the screen offset of the left top corner.</summary>
|
||||
/// <value>Screen offset to draw at, or <c>null</c> to use <see cref="ImGui.GetCursorScreenPos()"/>.</value>
|
||||
/// <value>Screen offset to draw at, or <c>null</c> to use <see cref="ImGui.GetCursorScreenPos()"/>, if no <see cref="TargetDrawList"/>
|
||||
/// is specified. Otherwise, you must specify it (for example, by passing <see cref="ImGui.GetCursorScreenPos()"/> when passing the window
|
||||
/// draw list.</value>
|
||||
public Vector2? ScreenOffset { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the font to use.</summary>
|
||||
/// <value>Font to use, or <c>null</c> to use <see cref="ImGui.GetFont"/> (the default).</value>
|
||||
/// <remarks>Must be set when specifying a target draw-list or drawing off the main thread.</remarks>
|
||||
public ImFontPtr? Font { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the font size.</summary>
|
||||
/// <value>Font size in pixels, or <c>0</c> to use the current ImGui font size <see cref="ImGui.GetFontSize"/>.
|
||||
/// </value>
|
||||
/// <remarks>Must be set when specifying a target draw-list or drawing off the main thread.</remarks>
|
||||
public float? FontSize { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the line height ratio.</summary>
|
||||
|
|
|
|||
|
|
@ -63,9 +63,22 @@ public unsafe ref struct SeStringDrawState
|
|||
else
|
||||
{
|
||||
this.drawList = ssdp.TargetDrawList.Value;
|
||||
this.ScreenOffset = Vector2.Zero;
|
||||
this.FontSize = ssdp.FontSize ?? throw new ArgumentException(
|
||||
$"{nameof(ssdp.FontSize)} must be set to render outside the main thread.");
|
||||
this.ScreenOffset = ssdp.ScreenOffset ?? Vector2.Zero;
|
||||
|
||||
// API14: Remove, always throw
|
||||
if (ThreadSafety.IsMainThread)
|
||||
{
|
||||
this.ScreenOffset = ssdp.ScreenOffset ?? ImGui.GetCursorScreenPos();
|
||||
this.FontSize = ssdp.FontSize ?? ImGui.GetFontSize();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"{nameof(ssdp.FontSize)} must be set when specifying a target draw list, as it cannot be fetched from the ImGui state.");
|
||||
}
|
||||
|
||||
// this.FontSize = ssdp.FontSize ?? throw new ArgumentException(
|
||||
// $"{nameof(ssdp.FontSize)} must be set when specifying a target draw list, as it cannot be fetched from the ImGui state.");
|
||||
this.WrapWidth = ssdp.WrapWidth ?? float.MaxValue;
|
||||
this.Color = ssdp.Color ?? uint.MaxValue;
|
||||
this.LinkHoverBackColor = 0; // Interactivity is unused outside the main thread.
|
||||
|
|
|
|||
|
|
@ -667,6 +667,8 @@ internal class DalamudInterface : IInternalDisposableService
|
|||
{
|
||||
if (this.isImGuiDrawDevMenu)
|
||||
{
|
||||
using var barColor = ImRaii.PushColor(ImGuiCol.WindowBg, new Vector4(0.060f, 0.060f, 0.060f, 0.773f));
|
||||
barColor.Push(ImGuiCol.MenuBarBg, Vector4.Zero);
|
||||
if (ImGui.BeginMainMenuBar())
|
||||
{
|
||||
var pluginManager = Service<PluginManager>.Get();
|
||||
|
|
@ -839,6 +841,11 @@ internal class DalamudInterface : IInternalDisposableService
|
|||
ImGui.PopStyleVar();
|
||||
}
|
||||
|
||||
if (ImGui.MenuItem("Raise external event through boot"))
|
||||
{
|
||||
ErrorHandling.CrashWithContext("Tést");
|
||||
}
|
||||
|
||||
ImGui.EndMenu();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -177,6 +177,24 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget
|
|||
ImGuiHelpers.SeStringWrapped(this.logkind.Value.Data.Span, this.style);
|
||||
}
|
||||
|
||||
if (ImGui.CollapsingHeader("Draw into drawlist"))
|
||||
{
|
||||
ImGuiHelpers.ScaledDummy(100);
|
||||
ImGui.SetCursorScreenPos(ImGui.GetItemRectMin() + ImGui.GetStyle().FramePadding);
|
||||
var clipMin = ImGui.GetItemRectMin() + ImGui.GetStyle().FramePadding;
|
||||
var clipMax = ImGui.GetItemRectMax() - ImGui.GetStyle().FramePadding;
|
||||
clipMin.Y = MathF.Max(clipMin.Y, ImGui.GetWindowPos().Y);
|
||||
clipMax.Y = MathF.Min(clipMax.Y, ImGui.GetWindowPos().Y + ImGui.GetWindowHeight());
|
||||
|
||||
var dl = ImGui.GetWindowDrawList();
|
||||
dl.PushClipRect(clipMin, clipMax);
|
||||
ImGuiHelpers.CompileSeStringWrapped(
|
||||
"<icon(1)>Test test<icon(1)>",
|
||||
new SeStringDrawParams
|
||||
{ Color = 0xFFFFFFFF, WrapWidth = float.MaxValue, TargetDrawList = dl });
|
||||
dl.PopClipRect();
|
||||
}
|
||||
|
||||
if (ImGui.CollapsingHeader("Addon Table"u8))
|
||||
{
|
||||
if (ImGui.BeginTable("Addon Sheet"u8, 3))
|
||||
|
|
|
|||
21
Dalamud/Utility/ErrorHandling.cs
Normal file
21
Dalamud/Utility/ErrorHandling.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Dalamud.Utility;
|
||||
|
||||
/// <summary>
|
||||
/// Utilities for handling errors inside Dalamud.
|
||||
/// </summary>
|
||||
internal static partial class ErrorHandling
|
||||
{
|
||||
/// <summary>
|
||||
/// Crash the game at this point, and show the crash handler with the supplied context.
|
||||
/// </summary>
|
||||
/// <param name="context">The context to show in the crash handler.</param>
|
||||
public static void CrashWithContext(string context)
|
||||
{
|
||||
BootVehRaiseExternalEvent(context);
|
||||
}
|
||||
|
||||
[LibraryImport("Dalamud.Boot.dll", EntryPoint = "BootVehRaiseExternalEventW", StringMarshalling = StringMarshalling.Utf16)]
|
||||
private static partial void BootVehRaiseExternalEvent(string info);
|
||||
}
|
||||
|
|
@ -1014,8 +1014,8 @@ int main() {
|
|||
config.pButtons = buttons;
|
||||
config.cButtons = ARRAYSIZE(buttons);
|
||||
config.nDefaultButton = IdButtonRestart;
|
||||
config.pszExpandedControlText = L"Hide stack trace";
|
||||
config.pszCollapsedControlText = L"Stack trace for plugin developers";
|
||||
config.pszExpandedControlText = L"Hide further information";
|
||||
config.pszCollapsedControlText = L"Further information for developers";
|
||||
config.pszExpandedInformation = window_log_str.c_str();
|
||||
config.pszWindowTitle = L"Dalamud Crash Handler";
|
||||
config.pRadioButtons = radios;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue