This commit is contained in:
VDH
2026-03-13 12:34:01 +07:00
parent fa92a237e7
commit 27da6c956f
33 changed files with 3529 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
# EditorConfig for Unity Project
root = true
[*]
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
[*.cs]
indent_style = space
indent_size = 4
end_of_line = crlf
# Unity Editor Only Usage Analyzer
# ERROR (màu đỏ) khi sử dụng code trong #if UNITY_EDITOR từ code không có directive
dotnet_diagnostic.UNITY_EDITOR_ONLY_USAGE.severity = error
# Các rule khác cho Unity
dotnet_analyzer_diagnostic.category-Unity.severity = warning
[*.{asmdef,asmref}]
indent_size = 2
[*.{json,md}]
indent_size = 2
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d6389bfe41ba69e42991434d87c1319f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+157
View File
@@ -0,0 +1,157 @@
#if UNITY_EDITOR
using System.IO;
using UnityEditor;
using UnityEngine;
/// <summary>
/// Tự động thêm UnityEditorOnlyAnalyzer vào các .csproj files khi Unity generate chúng
/// Analyzer phải ở project root, KHÔNG trong Assets/ để tránh Unity load như runtime assembly
/// </summary>
public class AddAnalyzerPostprocessor : AssetPostprocessor
{
private static string GetAnalyzerPath()
{
string projectRoot = Path.GetDirectoryName(Application.dataPath);
string analyzerPath = Path.Combine(projectRoot, "UnityEditorOnlyAnalyzer/bin/Release/netstandard2.0/UnityEditorOnlyAnalyzer.dll");
return Path.GetFullPath(analyzerPath);
}
/// <summary>
/// Unity gọi method này cho từng .csproj file được generate
/// Return content đã được modify - Unity sẽ ghi content này vào file
/// </summary>
public static string OnGeneratedCSProject(string path, string content)
{
string fileName = Path.GetFileName(path);
// Chỉ thêm vào các runtime assemblies, không thêm vào Editor assemblies
if (fileName != "Assembly-CSharp.csproj" && fileName != "Assembly-CSharp-firstpass.csproj")
{
return content;
}
// Kiểm tra xem đã có analyzer chưa
if (content.Contains("UnityEditorOnlyAnalyzer.dll"))
{
return content;
}
string analyzerPath = GetAnalyzerPath();
// Kiểm tra analyzer có tồn tại không
if (!File.Exists(analyzerPath))
{
Debug.LogWarning($"[UnityEditorOnlyAnalyzer] Analyzer not found at {analyzerPath}. " +
"Please build the analyzer first: cd UnityEditorOnlyAnalyzer && dotnet build -c Release");
return content;
}
// Bug fix 1: Tìm "</Project>" không có dấu cách ở đầu
int lastProjectTag = content.LastIndexOf("</Project>");
if (lastProjectTag < 0)
{
Debug.LogWarning($"[UnityEditorOnlyAnalyzer] Could not find </Project> tag in {fileName}");
return content;
}
// Bug fix 2: Không double-escape backslash trong XML - XML tự động escape
// Chỉ cần đảm bảo path có backslash đúng format Windows
string analyzerInclude = $@" <ItemGroup>
<Analyzer Include=""{analyzerPath}"" />
</ItemGroup>
</Project>";
string modifiedContent = content.Substring(0, lastProjectTag) + analyzerInclude;
Debug.Log($"[UnityEditorOnlyAnalyzer] ✅ Added analyzer to {fileName}");
return modifiedContent;
}
/// <summary>
/// Public method để menu item có thể gọi thủ công
/// </summary>
public static void AddAnalyzerToProjects()
{
// Trigger Unity regenerate .csproj files để OnGeneratedCSProject được gọi
UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal("", 0);
AssetDatabase.Refresh();
// Cũng gọi fallback method để đảm bảo
OnGeneratedCSProjectFiles();
}
/// <summary>
/// Backup method: Unity gọi method này sau khi generate tất cả .csproj files
/// Chỉ dùng nếu OnGeneratedCSProject không hoạt động
/// </summary>
private static void OnGeneratedCSProjectFiles()
{
// Fallback: patch files sau khi đã được generate
string projectRoot = Path.GetDirectoryName(Application.dataPath);
string analyzerPath = GetAnalyzerPath();
if (!File.Exists(analyzerPath))
{
Debug.LogWarning($"[UnityEditorOnlyAnalyzer] Analyzer not found at {analyzerPath}. " +
"Please build the analyzer first: cd UnityEditorOnlyAnalyzer && dotnet build -c Release");
return;
}
string[] csprojFiles = Directory.GetFiles(projectRoot, "*.csproj", SearchOption.TopDirectoryOnly);
bool anyModified = false;
foreach (string csprojFile in csprojFiles)
{
string fileName = Path.GetFileName(csprojFile);
if (fileName == "Assembly-CSharp.csproj" || fileName == "Assembly-CSharp-firstpass.csproj")
{
string content = File.ReadAllText(csprojFile);
if (!content.Contains("UnityEditorOnlyAnalyzer.dll"))
{
// Bug fix: Tìm "</Project>" không có dấu cách
int lastProjectTag = content.LastIndexOf("</Project>");
if (lastProjectTag >= 0)
{
string analyzerInclude = $@" <ItemGroup>
<Analyzer Include=""{analyzerPath}"" />
</ItemGroup>
</Project>";
content = content.Substring(0, lastProjectTag) + analyzerInclude;
File.WriteAllText(csprojFile, content);
anyModified = true;
Debug.Log($"[UnityEditorOnlyAnalyzer] ✅ Added analyzer to {fileName} (fallback method)");
}
}
}
}
if (anyModified)
{
Debug.Log("[UnityEditorOnlyAnalyzer] ✅ Analyzer added to .csproj files! Please reload your IDE.");
}
}
}
/// <summary>
/// Menu item để trigger thủ công việc thêm analyzer
/// </summary>
public class UnityEditorOnlyAnalyzerMenu
{
[MenuItem("Tools/Unity Editor Only Analyzer/Add Analyzer to Projects")]
public static void AddAnalyzerManually()
{
AddAnalyzerPostprocessor.AddAnalyzerToProjects();
}
[MenuItem("Tools/Unity Editor Only Analyzer/Regenerate Project Files")]
public static void RegenerateProjectFiles()
{
// Trigger Unity regenerate .csproj files
UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal("", 0);
AssetDatabase.Refresh();
Debug.Log("[UnityEditorOnlyAnalyzer] Project files regeneration triggered. Check Console for analyzer messages.");
}
}
#endif
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f4597c784f3bab545bcae652cd8541d9
+91
View File
@@ -0,0 +1,91 @@
# Cách Làm Analyzer Hiển Thị Màu Đỏ (Error)
Analyzer đã được đổi từ Warning → Error nhưng Visual Studio vẫn hiển thị màu xanh do cache.
## Bước 1: Xóa Cache Visual Studio
```powershell
# Đóng Visual Studio trước khi chạy
Remove-Item "E:\Projects\perfect-world-unity\.vs" -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item "E:\Projects\perfect-world-unity\obj" -Recurse -Force -ErrorAction SilentlyContinue
```
## Bước 2: Trigger Unity Regenerate .csproj
Trong Unity Editor:
1. Tools > Unity Editor Only Analyzer > Regenerate Project Files
2. Hoặc: Assets > Open C# Project
## Bước 3: Mở Visual Studio
1. Đóng Visual Studio hoàn toàn (nếu đang mở)
2. Double-click vào `perfect-world-unity.sln`
3. Đợi Visual Studio load xong
## Bước 4: Kiểm tra
1. Mở file `CdlgQuickBar.cs`
2. Dòng 42: `GetCurPanel1()` sẽ có:
- Squiggly line màu đỏ
- Icon error màu đỏ bên trái margin
- Error List hiển thị error (không phải warning)
## Nếu vẫn màu xanh
### Kiểm tra 1: .csproj có analyzer mới không?
Mở `Assembly-CSharp.csproj`, tìm dòng:
```xml
<Analyzer Include="E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll" />
```
Kiểm tra file modified date:
```powershell
(Get-Item "E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll").LastWriteTime
```
Phải là thời gian vừa build (vài phút trước).
### Kiểm tra 2: Visual Studio Options
1. Tools > Options
2. Text Editor > C# > Advanced
3. Đảm bảo "Enable full solution analysis" = checked
4. Đảm bảo "Run code analysis in separate process" = checked
### Kiểm tra 3: Error List Settings
1. View > Error List
2. Đảm bảo "Build + IntelliSense" được chọn (không chỉ "Build Only")
3. Filter: đảm bảo không filter out errors
### Kiểm tra 4: Severity trong EditorConfig
File `.editorconfig` có thể override severity:
```ini
# Nếu có dòng này, xóa đi hoặc đổi thành error
dotnet_diagnostic.UNITY_EDITOR_ONLY_USAGE.severity = error
```
## Cách test nhanh
Tạo file test đơn giản:
```csharp
// Assets/TestError.cs
#if UNITY_EDITOR
public class TestEditor
{
public static void TestMethod() { }
}
#endif
public class TestRegular
{
public void Test()
{
TestEditor.TestMethod(); // Phải có error màu đỏ ở đây
}
}
```
Nếu file test này cũng màu xanh → vấn đề là VS cache hoặc settings.
+108
View File
@@ -0,0 +1,108 @@
# Giải Pháp: Analyzer Bị Mất Sau Khi Reload Visual Studio
## Vấn Đề
Unity tự động regenerate các file `.csproj`**ghi đè** các thay đổi thủ công. Đây là lý do analyzer bị mất mỗi khi bạn mở lại Visual Studio.
## Giải Pháp: Sử Dụng Cách Chính Thức Của Unity ✅
Unity hỗ trợ Roslyn Analyzers thông qua **Asset Labels**. Đây là cách đúng và bền vững nhất.
### Bước 1: Analyzer Đã Được Copy Vào Assets ✅
Analyzer DLL đã được copy vào `Assets/Analyzers/UnityEditorOnlyAnalyzer.dll`
### Bước 2: Thiết Lập Label trong Unity Editor
1. **Mở Unity Editor**
2. **Tìm file** `Assets/Analyzers/UnityEditorOnlyAnalyzer.dll` trong Project window
3. **Click chọn file** này
4. **Mở Inspector** (nếu chưa mở)
5. **Ở phần Labels** (phía dưới Inspector):
- Click vào label dropdown
- Chọn hoặc tạo label: **`RoslynAnalyzer`**
- Hoặc gõ trực tiếp: `RoslynAnalyzer`
6. **Unity sẽ tự động**:
- Thêm analyzer vào tất cả `.csproj` files
- Giữ analyzer ngay cả khi regenerate `.csproj`
### Bước 3: Kiểm Tra
Sau khi set label:
1. **Kiểm tra Console** trong Unity - sẽ có log từ `AddAnalyzerPostprocessor`
2. **Mở file** `Assembly-CSharp.csproj` và tìm dòng có `UnityEditorOnlyAnalyzer.dll`
3. **Đóng và mở lại Visual Studio** - analyzer vẫn còn!
## Script Tự Động
Script `AddAnalyzerPostprocessor.cs` đã được cập nhật để:
1. **Tự động set label** `RoslynAnalyzer` khi Unity generate `.csproj`
2. **Backup method**: Vẫn thêm analyzer vào `.csproj` nếu label không hoạt động
### Trigger Thủ Công (Nếu Cần)
Nếu script không tự động chạy, bạn có thể trigger thủ công:
1. **Trong Unity Editor**:
- Menu: **Tools > Unity Editor Only Analyzer > Add Analyzer to Projects**
- Hoặc: **Tools > Unity Editor Only Analyzer > Regenerate Project Files**
2. **Kiểm tra Console** để xem log messages
## Cách Hoạt Động
### Cách Chính Thức (RoslynAnalyzer Label)
Unity tự động:
1. Scan tất cả files trong `Assets/` có label `RoslynAnalyzer`
2. Thêm chúng vào `<ItemGroup><Analyzer>` trong `.csproj`
3. **Giữ nguyên** ngay cả khi regenerate `.csproj`
### Backup Method (Script)
Script `OnGeneratedCSProjectFiles()` sẽ:
1. Được gọi mỗi khi Unity generate `.csproj`
2. Tự động thêm analyzer vào `.csproj` nếu chưa có
3. Set label `RoslynAnalyzer` nếu chưa có
## Troubleshooting
### Analyzer Vẫn Bị Mất?
1. **Kiểm tra label**:
- Chọn file `UnityEditorOnlyAnalyzer.dll` trong Unity
- Xem Inspector có label `RoslynAnalyzer` không
- Nếu không có, thêm thủ công
2. **Kiểm tra file tồn tại**:
```powershell
Test-Path "E:\Projects\perfect-world-unity\Assets\Analyzers\UnityEditorOnlyAnalyzer.dll"
```
3. **Reimport asset**:
- Click phải vào `UnityEditorOnlyAnalyzer.dll` trong Unity
- Chọn "Reimport"
4. **Trigger script thủ công**:
- Tools > Unity Editor Only Analyzer > Add Analyzer to Projects
- Kiểm tra Console để xem log
5. **Kiểm tra Unity Version**:
- Label `RoslynAnalyzer` hỗ trợ từ Unity 2019.2+
- Nếu dùng Unity cũ hơn, chỉ có thể dùng script method
### Visual Studio Không Thấy Analyzer?
1. **Đóng Visual Studio hoàn toàn**
2. **Xóa folder `obj`** trong project root (nếu có)
3. **Mở lại Visual Studio**
4. **Đợi project load xong** (có thể mất vài giây)
5. **Kiểm tra Error List** (View > Error List)
## Kết Luận
**Cách tốt nhất**: Sử dụng label `RoslynAnalyzer` - đây là cách chính thức của Unity và sẽ không bị mất khi regenerate `.csproj`.
Script `AddAnalyzerPostprocessor.cs` sẽ tự động set label này mỗi khi Unity generate `.csproj`, nhưng bạn cũng có thể set thủ công trong Unity Editor để đảm bảo.
+101
View File
@@ -0,0 +1,101 @@
# Giải Pháp Lỗi: Analyzer DLL Không Load Được
## Vấn Đề
Khi đặt analyzer DLL trong `Assets/`, Unity cố gắng load nó như một **runtime assembly** và gặp lỗi:
```
Unable to resolve reference 'Microsoft.CodeAnalysis'
Unable to resolve reference 'System.Collections.Immutable'
Unable to resolve reference 'Microsoft.CodeAnalysis.CSharp'
```
## Nguyên Nhân
**Roslyn Analyzers KHÔNG phải runtime assemblies** - chúng chỉ được IDE (Visual Studio/VS Code) sử dụng để phân tích code tại design-time. Unity runtime không có các dependencies của Roslyn (Microsoft.CodeAnalysis, etc.).
## Giải Pháp ✅
### Analyzer Phải Ở NGOÀI Assets/
Analyzer DLL **KHÔNG được đặt trong `Assets/`**. Thay vào đó:
1. **Đặt analyzer ở project root**: `UnityEditorOnlyAnalyzer/bin/Release/netstandard2.0/`
2. **Script tự động thêm vào `.csproj`** mỗi khi Unity generate
3. **IDE sẽ load analyzer** từ đường dẫn trong `.csproj`
### Cách Hoạt Động
```
Unity Generate .csproj
Script OnGeneratedCSProjectFiles() được gọi
Script thêm analyzer vào .csproj với đường dẫn tuyệt đối
Visual Studio/VS Code load analyzer từ .csproj
Analyzer hoạt động trong IDE (không phải Unity runtime)
```
## Đã Sửa
1.**Xóa analyzer khỏi `Assets/Analyzers/`**
2.**Cập nhật script** để chỉ sử dụng analyzer từ project root
3.**Script tự động thêm analyzer vào `.csproj`** mỗi khi Unity generate
## Kiểm Tra
1. **Mở Unity Editor**
2. **Kiểm tra Console** - không còn lỗi về analyzer DLL
3. **Trigger script**:
- Tools > Unity Editor Only Analyzer > Add Analyzer to Projects
4. **Mở `Assembly-CSharp.csproj`** - sẽ thấy analyzer được thêm:
```xml
<Analyzer Include="E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll" />
```
5. **Reload Visual Studio** - analyzer sẽ hoạt động
## Lưu Ý Quan Trọng
- ✅ **ĐÚNG**: Analyzer ở project root, reference trong `.csproj`
- ❌ **SAI**: Analyzer trong `Assets/` - Unity sẽ cố load và gặp lỗi
- ✅ **ĐÚNG**: IDE (Visual Studio) load analyzer từ `.csproj`
- ❌ **SAI**: Unity runtime load analyzer từ `Assets/`
## Troubleshooting
### Analyzer Vẫn Bị Mất Sau Reload?
Script `OnGeneratedCSProjectFiles()` sẽ tự động thêm lại analyzer mỗi khi Unity generate `.csproj`. Nếu vẫn bị mất:
1. **Kiểm tra script có chạy không**:
- Tools > Unity Editor Only Analyzer > Add Analyzer to Projects
- Xem Console để kiểm tra log
2. **Kiểm tra đường dẫn analyzer**:
```powershell
Test-Path "E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll"
```
3. **Rebuild analyzer nếu cần**:
```powershell
cd E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer
dotnet build -c Release
```
### Visual Studio Không Thấy Analyzer?
1. **Đóng Visual Studio hoàn toàn**
2. **Xóa folder `obj`** trong project root (nếu có)
3. **Mở lại Visual Studio**
4. **Đợi project load xong**
5. **Kiểm tra Error List** (View > Error List)
## Kết Luận
Analyzer đã được sửa để hoạt động đúng cách:
- ✅ Không còn trong `Assets/` (tránh Unity load như runtime assembly)
- ✅ Tự động thêm vào `.csproj` mỗi khi Unity generate
- ✅ IDE sẽ load và sử dụng analyzer để hiển thị warnings
+171
View File
@@ -0,0 +1,171 @@
# Hướng Dẫn Sử Dụng Unity Editor Only Analyzer
## ✅ Tình Trạng Hiện Tại
**Analyzer đã được thêm vào `Assembly-CSharp.csproj`!**
File `.csproj` hiện đã có analyzer tại dòng 62:
```xml
<Analyzer Include="E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll" />
```
## 🔄 Cách Unity Generate .csproj Files
Unity tự động generate các file `.csproj` khi:
1. **Mở project lần đầu** - Unity scan tất cả scripts và tạo `.csproj` files
2. **Thêm/xóa script files** - Unity tự động regenerate
3. **Thay đổi External Script Editor** - Edit > Preferences > External Tools > External Script Editor
4. **Gọi menu "Open C# Project"** - Assets > Open C# Project (hoặc double-click vào `.csproj`)
5. **Reimport scripts** - Click phải vào folder Scripts > Reimport
### Script `AddAnalyzerPostprocessor.cs` hoạt động như thế nào?
Script này sử dụng `AssetPostprocessor.OnGeneratedCSProjectFiles()` - một callback mà Unity gọi **SAU KHI** đã generate xong tất cả `.csproj` files.
Khi Unity generate `.csproj`:
1. Unity tạo các file `.csproj` mới (ghi đè file cũ)
2. Unity gọi `OnGeneratedCSProjectFiles()`
3. Script của chúng ta đọc file `.csproj` vừa tạo
4. Thêm analyzer vào `<ItemGroup>` nếu chưa có
5. Ghi lại file
## 🚀 Cách Kích Hoạt Analyzer
### Cách 1: Reload Project trong Visual Studio (Khuyến nghị)
1. **Đóng Visual Studio** (nếu đang mở)
2. **Mở lại Visual Studio** bằng cách:
- Double-click vào `perfect-world-unity.sln`
- Hoặc trong Unity: Assets > Open C# Project
3. Visual Studio sẽ tự động load analyzer và hiển thị warnings
### Cách 2: Trigger Unity Generate Lại .csproj
Nếu analyzer chưa được thêm tự động, bạn có thể trigger Unity generate lại:
**Trong Unity Editor:**
1. Mở Unity Editor
2. Vào menu: **Tools > Unity Editor Only Analyzer > Regenerate Project Files**
3. Hoặc: **Assets > Open C# Project**
4. Kiểm tra Console để xem log từ `AddAnalyzerPostprocessor`
**Hoặc thủ công:**
1. Xóa các file `.csproj``.sln` trong project root
2. Mở lại Unity Editor
3. Unity sẽ tự động generate lại
### Cách 3: Thêm Thủ Công (Đã làm)
Analyzer đã được thêm trực tiếp vào `Assembly-CSharp.csproj` ở dòng 62.
## ✅ Kiểm Tra Analyzer Đã Hoạt Động
### Bước 1: Tạo File Test
Tạo file `Assets/TestEditorOnly.cs`:
```csharp
#if UNITY_EDITOR
public class EditorOnlyTestClass
{
public static void EditorOnlyMethod()
{
UnityEngine.Debug.Log("This is editor only");
}
}
#endif
public class RegularTestClass
{
public void TestMethod()
{
// ⚠️ Dòng này nên có warning trong Visual Studio
EditorOnlyTestClass.EditorOnlyMethod();
}
}
```
### Bước 2: Mở trong Visual Studio
1. Mở file `TestEditorOnly.cs` trong Visual Studio
2. Bạn sẽ thấy **squiggly line** (gạch chân màu vàng/xanh) dưới `EditorOnlyMethod()`
3. Hover chuột sẽ thấy warning:
```
UNITY_EDITOR_ONLY_USAGE: Method/Type 'EditorOnlyMethod' is only available in UNITY_EDITOR and may cause build errors
```
### Bước 3: Kiểm Tra Error List
Trong Visual Studio:
1. View > Error List (hoặc Ctrl+W, E)
2. Chọn "Warnings" tab
3. Tìm warning với ID `UNITY_EDITOR_ONLY_USAGE`
## 🔧 Troubleshooting
### Analyzer không hiển thị warnings?
1. **Kiểm tra analyzer DLL tồn tại:**
```powershell
Test-Path "E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll"
```
Phải trả về `True`
2. **Kiểm tra .csproj có analyzer:**
- Mở `Assembly-CSharp.csproj`
- Tìm dòng có `UnityEditorOnlyAnalyzer.dll`
- Đảm bảo đường dẫn đúng
3. **Reload project:**
- Đóng Visual Studio
- Xóa folder `obj` và `bin` trong project (nếu có)
- Mở lại Visual Studio
4. **Kiểm tra Error List:**
- View > Error List
- Đảm bảo "Build + IntelliSense" được chọn
- Kiểm tra cả "Errors", "Warnings", "Messages"
5. **Kiểm tra Output window:**
- View > Output
- Chọn "Show output from: Build" hoặc "IntelliSense"
- Xem có lỗi load analyzer không
### Script không tự động thêm analyzer?
1. **Kiểm tra script có trong project:**
- File phải ở `Assets/Editor/AddAnalyzerPostprocessor.cs`
- Phải có `#if UNITY_EDITOR` ở đầu
2. **Trigger thủ công:**
- Tools > Unity Editor Only Analyzer > Add Analyzer to Projects
- Kiểm tra Console trong Unity để xem log
3. **Kiểm tra đường dẫn:**
- Script dùng `Application.dataPath` để tìm project root
- Đảm bảo cấu trúc folder đúng:
```
perfect-world-unity/
├── Assets/
├── UnityEditorOnlyAnalyzer/
│ └── bin/Release/netstandard2.0/
│ └── UnityEditorOnlyAnalyzer.dll
└── Assembly-CSharp.csproj
```
## 📝 Lưu Ý Quan Trọng
1. **Analyzer chỉ hoạt động trong IDE** (Visual Studio/VS Code), không phải trong Unity Editor
2. **Cần reload project** sau khi thêm analyzer vào `.csproj`
3. **Unity có thể ghi đè `.csproj`** khi generate lại - script sẽ tự động thêm lại analyzer
4. **Warnings chỉ hiển thị khi code thực sự có vấn đề** - đảm bảo bạn đang test với code có `#if UNITY_EDITOR`
## 🎯 Kết Luận
Analyzer đã được cấu hình và sẵn sàng sử dụng! Chỉ cần:
1. Reload project trong Visual Studio
2. Tạo file test để kiểm tra
3. Xem warnings trong Error List
Nếu vẫn không thấy warnings, hãy kiểm tra các bước troubleshooting ở trên.
+174
View File
@@ -0,0 +1,174 @@
# Hướng Dẫn Thiết Lập Cảnh Báo #if UNITY_EDITOR
Tài liệu này hướng dẫn cách thiết lập IDE để cảnh báo khi sử dụng hàm/method được wrap trong `#if UNITY_EDITOR` từ code không có directive này, có thể gây lỗi build.
## Phương Pháp 1: Custom Roslyn Analyzer (Khuyến nghị)
### Bước 1: Build Analyzer
Mở PowerShell hoặc Command Prompt và chạy:
```powershell
cd E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer
dotnet build -c Release
```
### Bước 2: Thêm Analyzer vào Unity Project
Sau khi Unity generate lại `.csproj` files, bạn cần thêm analyzer vào các file `.csproj` chính:
**Cách tự động (sử dụng AssetPostprocessor):**
Tạo file `Assets/Editor/AddAnalyzerPostprocessor.cs`:
```csharp
using System.IO;
using UnityEditor;
using UnityEngine;
public class AddAnalyzerPostprocessor : AssetPostprocessor
{
private static void OnGeneratedCSProjectFiles()
{
string analyzerPath = Path.GetFullPath("UnityEditorOnlyAnalyzer/bin/Release/netstandard2.0/UnityEditorOnlyAnalyzer.dll");
if (!File.Exists(analyzerPath))
{
Debug.LogWarning($"Analyzer not found at {analyzerPath}. Please build the analyzer first.");
return;
}
string[] csprojFiles = Directory.GetFiles(".", "*.csproj", SearchOption.TopDirectoryOnly);
foreach (string csprojFile in csprojFiles)
{
if (csprojFile.Contains("Assembly-CSharp") || csprojFile.Contains("Assembly-CSharp-firstpass"))
{
string content = File.ReadAllText(csprojFile);
if (!content.Contains("UnityEditorOnlyAnalyzer.dll"))
{
string analyzerInclude = $@" <ItemGroup>
<Analyzer Include=""{analyzerPath.Replace("\\", "\\\\")}"" />
</ItemGroup>
</Project>";
content = content.Replace(" </Project>", analyzerInclude);
File.WriteAllText(csprojFile, content);
}
}
}
}
}
```
**Cách thủ công:**
Mở file `Assembly-CSharp.csproj` và thêm vào trước thẻ `</Project>`:
```xml
<ItemGroup>
<Analyzer Include="E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll" />
</ItemGroup>
</Project>
```
### Bước 3: Reload Project trong Visual Studio
1. Đóng Visual Studio
2. Mở lại Unity project
3. Visual Studio sẽ tự động reload và áp dụng analyzer
## Phương Pháp 2: Sử dụng EditorConfig (Hỗ trợ một phần)
File `.editorconfig` đã được tạo ở root project. Một số IDE (như Visual Studio 2019+, VS Code với C# extension) sẽ đọc file này.
Tuy nhiên, EditorConfig không thể tự động phát hiện `#if UNITY_EDITOR` - bạn vẫn cần Roslyn Analyzer.
## Phương Pháp 3: Visual Studio Code Rules (Nếu dùng VS Code)
Nếu bạn sử dụng VS Code với C# extension, analyzer sẽ tự động hoạt động sau khi được thêm vào `.csproj`.
Đảm bảo bạn có extension:
- **C#** (Microsoft)
- **C# Dev Kit** (Microsoft) - Khuyến nghị
## Kiểm Tra
Sau khi setup, tạo file test:
```csharp
// TestFile.cs
#if UNITY_EDITOR
public class EditorOnlyClass
{
public static void EditorMethod() { }
}
#endif
public class RegularClass
{
public void Test()
{
EditorOnlyClass.EditorMethod(); // ⚠️ Nên có cảnh báo ở đây
}
}
```
Bạn sẽ thấy warning:
```
UNITY_EDITOR_ONLY_USAGE: Method/Type 'EditorMethod' is only available in UNITY_EDITOR and may cause build errors
```
## Troubleshooting
### Analyzer không hoạt động
1. **Kiểm tra analyzer đã được build chưa:**
```powershell
Test-Path "UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll"
```
2. **Kiểm tra đường dẫn trong .csproj:**
- Đường dẫn phải là absolute path hoặc relative path đúng
- Đảm bảo file `.dll` tồn tại
3. **Reload project:**
- Đóng và mở lại Visual Studio/VS Code
- Hoặc trong Visual Studio: Project > Reload Project
4. **Kiểm tra Output window:**
- Trong Visual Studio, xem Output window để xem có lỗi load analyzer không
### Warning không hiển thị
1. Đảm bảo code thực sự có vấn đề (method trong `#if UNITY_EDITOR` được gọi từ code không có directive)
2. Kiểm tra Error List trong Visual Studio (View > Error List)
3. Đảm bảo Warning level không bị tắt trong project settings
## Tùy Chỉnh Severity
Bạn có thể thay đổi severity từ Warning sang Error trong `.editorconfig`:
```ini
[*.cs]
dotnet_diagnostic.UNITY_EDITOR_ONLY_USAGE.severity = error
```
Hoặc disable hoàn toàn:
```ini
[*.cs]
dotnet_diagnostic.UNITY_EDITOR_ONLY_USAGE.severity = none
```
## Lưu Ý
- Analyzer chỉ hoạt động khi IDE đang mở project
- Unity build process có thể không hiển thị warnings này, nhưng Visual Studio sẽ hiển thị
- Nếu bạn sử dụng CI/CD, cần đảm bảo analyzer được include trong build process
## Tài Liệu Tham Khảo
- [Roslyn Analyzers Documentation](https://github.com/dotnet/roslyn-analyzers)
- [Unity Code Analysis](https://github.com/microsoft/Microsoft.Unity.Analyzers)
+125
View File
@@ -0,0 +1,125 @@
# Kiểm Tra Analyzer Hoạt Động
## Vấn Đề Hiện Tại
Trong file `CdlgQuickBar.cs`:
- **Dòng 42**: `GetCurPanel1()` được gọi từ `UpdateShortcuts()` (KHÔNG có `#if UNITY_EDITOR`)
- **Dòng 440-444**: `GetCurPanel1()` được định nghĩa TRONG `#if UNITY_EDITOR`
Đây là trường hợp analyzer cần phát hiện!
## Các Bước Kiểm Tra
### Bước 1: Đảm Bảo Analyzer Đã Được Thêm Vào .csproj ✅
Analyzer đã được thêm vào `Assembly-CSharp.csproj` tại dòng 62:
```xml
<Analyzer Include="E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll" />
```
### Bước 2: Rebuild Analyzer ✅
Analyzer đã được rebuild với logic cải thiện.
### Bước 3: Reload Project trong Visual Studio
**QUAN TRỌNG**: Bạn cần reload project để analyzer hoạt động:
1. **Đóng Visual Studio hoàn toàn**
2. **Mở lại Visual Studio** bằng cách:
- Double-click vào `perfect-world-unity.sln`
- Hoặc từ Unity: Assets > Open C# Project
3. **Đợi Visual Studio load project** (có thể mất vài giây)
### Bước 4: Kiểm Tra Warnings
Sau khi Visual Studio load xong:
1. **Mở file `CdlgQuickBar.cs`** trong Visual Studio
2. **Tìm dòng 42**: `int nCurPanel9 = GetCurPanel1();`
3. **Bạn sẽ thấy**:
- Squiggly line (gạch chân màu vàng/xanh) dưới `GetCurPanel1()`
- Hover chuột sẽ thấy warning message
4. **Mở Error List**:
- View > Error List (hoặc Ctrl+W, E)
- Chọn tab "Warnings"
- Tìm warning với ID `UNITY_EDITOR_ONLY_USAGE`
- Message: `Method/Type 'GetCurPanel1' is only available in UNITY_EDITOR and may cause build errors`
### Bước 5: Nếu Vẫn Không Thấy Warning
#### Kiểm tra 1: Analyzer DLL tồn tại
```powershell
Test-Path "E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll"
```
Phải trả về `True`
#### Kiểm tra 2: .csproj có analyzer
Mở `Assembly-CSharp.csproj` và tìm dòng có `UnityEditorOnlyAnalyzer.dll`
#### Kiểm tra 3: Output Window
1. View > Output
2. Chọn "Show output from: Build" hoặc "IntelliSense"
3. Xem có lỗi load analyzer không
#### Kiểm tra 4: Force Reload
1. Đóng Visual Studio
2. Xóa folder `obj` trong project root (nếu có)
3. Mở lại Visual Studio
#### Kiểm tra 5: Test với File Đơn Giản
Tạo file test `Assets/TestAnalyzer.cs`:
```csharp
#if UNITY_EDITOR
public class EditorOnlyTest
{
public static void TestMethod() { }
}
#endif
public class RegularClass
{
public void Test()
{
EditorOnlyTest.TestMethod(); // ⚠️ Nên có warning ở đây
}
}
```
Nếu file test này có warning nhưng `CdlgQuickBar.cs` không có, có thể là vấn đề với logic analyzer.
## Debug Analyzer
Nếu analyzer vẫn không hoạt động, có thể cần debug:
1. **Kiểm tra Visual Studio Version**: Analyzer cần Visual Studio 2019+ hoặc VS Code với C# extension
2. **Kiểm tra .NET SDK**: Cần .NET SDK để build analyzer
3. **Kiểm tra Logs**: Xem Output window trong Visual Studio
## Giải Pháp Tạm Thời
Nếu analyzer không hoạt động ngay, bạn có thể:
1. **Wrap code gọi trong `#if UNITY_EDITOR`**:
```csharp
#if UNITY_EDITOR
int nCurPanel9 = GetCurPanel1();
#else
int nCurPanel9 = 1; // Default value
#endif
```
2. **Hoặc di chuyển `GetCurPanel1()` ra ngoài `#if UNITY_EDITOR`** nếu nó cần được gọi từ runtime code.
## Kết Luận
Analyzer đã được cải thiện và rebuild. Bạn cần:
1. ✅ Analyzer đã được thêm vào .csproj
2. ✅ Analyzer đã được rebuild
3.**Reload Visual Studio** để analyzer hoạt động
4. ⏳ Kiểm tra warnings trong Error List
Sau khi reload Visual Studio, bạn sẽ thấy warning ở dòng 42 của `CdlgQuickBar.cs`.
+61
View File
@@ -0,0 +1,61 @@
# Quick Start - Unity Editor Only Analyzer
## Bước 1: Analyzer đã được build ✅
Analyzer đã được build thành công tại:
```
E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll
```
## Bước 2: Kích hoạt Analyzer
### Tự động (Khuyến nghị):
1. Mở Unity Editor
2. File `Assets/Editor/AddAnalyzerPostprocessor.cs` sẽ tự động thêm analyzer vào `.csproj` files khi Unity generate chúng
3. Reload project trong Visual Studio/VS Code
### Thủ công:
Mở file `Assembly-CSharp.csproj` và thêm trước thẻ `</Project>`:
```xml
<ItemGroup>
<Analyzer Include="E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll" />
</ItemGroup>
</Project>
```
## Bước 3: Kiểm tra
Tạo file test để kiểm tra:
```csharp
#if UNITY_EDITOR
public class EditorOnlyClass
{
public static void EditorMethod() { }
}
#endif
public class RegularClass
{
public void Test()
{
EditorOnlyClass.EditorMethod(); // ⚠️ Nên có warning ở đây
}
}
```
Bạn sẽ thấy warning trong Visual Studio/VS Code:
```
UNITY_EDITOR_ONLY_USAGE: Method/Type 'EditorMethod' is only available in UNITY_EDITOR and may cause build errors
```
## Lưu ý
- Analyzer chỉ hoạt động trong IDE (Visual Studio/VS Code), không phải trong Unity Editor
- Cần reload project sau khi thêm analyzer
- Nếu không thấy warnings, kiểm tra Error List trong Visual Studio (View > Error List)
## Troubleshooting
Nếu analyzer không hoạt động, xem file `SETUP_UNITY_EDITOR_ANALYZER.md` để biết chi tiết.
+88
View File
@@ -0,0 +1,88 @@
# Unity Editor Only Usage Analyzer
Roslyn Analyzer để phát hiện việc sử dụng code được wrap trong `#if UNITY_EDITOR` từ code không có directive tương ứng, có thể gây lỗi build.
## Cài đặt
### Cách 1: Build và thêm vào .csproj (Khuyến nghị)
1. Build analyzer project:
```bash
cd UnityEditorOnlyAnalyzer
dotnet build -c Release
```
2. Thêm analyzer vào Unity project bằng cách chỉnh sửa `.csproj` files:
```xml
<ItemGroup>
<Analyzer Include="$(SolutionDir)UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll" />
</ItemGroup>
```
### Cách 2: Sử dụng như NuGet Package
1. Tạo NuGet package:
```bash
dotnet pack -c Release
```
2. Thêm vào `packages.config` hoặc sử dụng PackageReference trong .csproj
## Cách hoạt động
Analyzer sẽ cảnh báo khi:
- Một method được định nghĩa trong `#if UNITY_EDITOR` nhưng được gọi từ code không có directive này
- Một class được định nghĩa trong `#if UNITY_EDITOR` nhưng được khởi tạo từ code không có directive này
- Một property/field được định nghĩa trong `#if UNITY_EDITOR` nhưng được truy cập từ code không có directive này
## Ví dụ
### ❌ Code sẽ bị cảnh báo:
```csharp
#if UNITY_EDITOR
public void EditorOnlyMethod() { }
#endif
public void RegularMethod()
{
EditorOnlyMethod(); // ⚠️ Cảnh báo: EditorOnlyMethod chỉ có trong UNITY_EDITOR
}
```
### ✅ Code đúng:
```csharp
#if UNITY_EDITOR
public void EditorOnlyMethod() { }
#endif
#if UNITY_EDITOR
public void RegularMethod()
{
EditorOnlyMethod(); // ✅ OK: Cả hai đều trong UNITY_EDITOR
}
#endif
```
## Diagnostic ID
- **ID**: `UNITY_EDITOR_ONLY_USAGE`
- **Severity**: Warning
- **Category**: Unity
## Tùy chỉnh
Bạn có thể disable warning này trong `.editorconfig`:
```ini
[*.cs]
dotnet_diagnostic.UNITY_EDITOR_ONLY_USAGE.severity = none
```
Hoặc trong code:
```csharp
#pragma warning disable UNITY_EDITOR_ONLY_USAGE
// Your code here
#pragma warning restore UNITY_EDITOR_ONLY_USAGE
```
@@ -0,0 +1,301 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class UnityEditorOnlyUsageAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "UNITY_EDITOR_ONLY_USAGE";
private static readonly LocalizableString Title =
"Usage of UNITY_EDITOR-only code";
private static readonly LocalizableString MessageFormat =
"Method/Type '{0}' is only available in UNITY_EDITOR and may cause build errors";
private static readonly LocalizableString Description =
"Warns when code wrapped in #if UNITY_EDITOR is used from code that is not wrapped in the same directive.";
private const string Category = "Unity";
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
DiagnosticId,
Title,
MessageFormat,
Category,
DiagnosticSeverity.Error, // Changed from Warning to Error để hiển thị màu đỏ
isEnabledByDefault: true,
description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
// Kiểm tra method calls
context.RegisterSyntaxNodeAction(AnalyzeInvocation, SyntaxKind.InvocationExpression);
// Kiểm tra property/field access
context.RegisterSyntaxNodeAction(AnalyzeMemberAccess, SyntaxKind.SimpleMemberAccessExpression);
// Kiểm tra identifier (biến đơn giản như m_nCurPanel2)
context.RegisterSyntaxNodeAction(AnalyzeIdentifier, SyntaxKind.IdentifierName);
// Kiểm tra object creation (new ClassName())
context.RegisterSyntaxNodeAction(AnalyzeObjectCreation, SyntaxKind.ObjectCreationExpression);
}
private void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
{
var invocation = (InvocationExpressionSyntax)context.Node;
if (invocation.Expression is MemberAccessExpressionSyntax memberAccess)
{
CheckMemberAccess(context, memberAccess, invocation);
}
else if (invocation.Expression is IdentifierNameSyntax identifier)
{
CheckIdentifier(context, identifier, invocation);
}
}
private void AnalyzeMemberAccess(SyntaxNodeAnalysisContext context)
{
var memberAccess = (MemberAccessExpressionSyntax)context.Node;
CheckMemberAccess(context, memberAccess, memberAccess);
}
private void AnalyzeIdentifier(SyntaxNodeAnalysisContext context)
{
var identifier = (IdentifierNameSyntax)context.Node;
// Bỏ qua namespace declarations
if (identifier.Parent is QualifiedNameSyntax ||
identifier.Parent is UsingDirectiveSyntax ||
identifier.Parent is NamespaceDeclarationSyntax ||
identifier.Parent is FileScopedNamespaceDeclarationSyntax)
{
return;
}
// Bỏ qua nếu identifier này là phần của member access (đã xử lý ở AnalyzeMemberAccess)
if (identifier.Parent is MemberAccessExpressionSyntax memberAccess && memberAccess.Name == identifier)
{
return;
}
// Bỏ qua nếu identifier này là phần của invocation (đã xử lý ở AnalyzeInvocation)
if (identifier.Parent is InvocationExpressionSyntax)
{
return;
}
// Bỏ qua type names trong declarations
if (identifier.Parent is VariableDeclarationSyntax ||
identifier.Parent is TypeSyntax)
{
return;
}
CheckIdentifier(context, identifier, identifier);
}
private void AnalyzeObjectCreation(SyntaxNodeAnalysisContext context)
{
var objectCreation = (ObjectCreationExpressionSyntax)context.Node;
if (objectCreation.Type != null)
{
var symbol = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol;
if (symbol != null && IsEditorOnlySymbol(symbol, context))
{
if (!IsInEditorOnlyContext(context.Node))
{
var diagnostic = Diagnostic.Create(
Rule,
objectCreation.Type.GetLocation(),
symbol.Name);
context.ReportDiagnostic(diagnostic);
}
}
}
}
private void CheckMemberAccess(SyntaxNodeAnalysisContext context, MemberAccessExpressionSyntax memberAccess, SyntaxNode reportNode)
{
var symbolInfo = context.SemanticModel.GetSymbolInfo(memberAccess);
if (symbolInfo.Symbol == null)
return;
// Check if the symbol is defined within #if UNITY_EDITOR
if (IsEditorOnlySymbol(symbolInfo.Symbol, context))
{
// Check if the current usage is NOT within #if UNITY_EDITOR
if (!IsInEditorOnlyContext(memberAccess))
{
var diagnostic = Diagnostic.Create(
Rule,
reportNode.GetLocation(),
symbolInfo.Symbol.Name);
context.ReportDiagnostic(diagnostic);
}
}
}
private void CheckIdentifier(SyntaxNodeAnalysisContext context, IdentifierNameSyntax identifier, SyntaxNode reportNode)
{
var symbolInfo = context.SemanticModel.GetSymbolInfo(identifier);
if (symbolInfo.Symbol == null)
return;
if (IsEditorOnlySymbol(symbolInfo.Symbol, context))
{
if (!IsInEditorOnlyContext(identifier))
{
var diagnostic = Diagnostic.Create(
Rule,
reportNode.GetLocation(),
symbolInfo.Symbol.Name);
context.ReportDiagnostic(diagnostic);
}
}
}
private bool IsEditorOnlySymbol(ISymbol symbol, SyntaxNodeAnalysisContext context)
{
if (symbol == null)
return false;
// Get the syntax reference
var syntaxReferences = symbol.DeclaringSyntaxReferences;
if (syntaxReferences.Length == 0)
return false;
foreach (var syntaxRef in syntaxReferences)
{
var syntax = syntaxRef.GetSyntax(context.CancellationToken);
if (IsInEditorOnlyContext(syntax))
{
return true;
}
}
return false;
}
private bool IsInEditorOnlyContext(SyntaxNode node)
{
if (node == null)
return false;
var root = node.SyntaxTree.GetRoot();
var nodeStart = node.SpanStart;
// Get all trivia in the file
var allTrivia = root.DescendantTrivia();
int activeIfDirectiveStart = -1;
bool inEditorBlock = false;
foreach (var trivia in allTrivia)
{
// Stop checking if we've passed the node
if (trivia.SpanStart > nodeStart)
break;
if (trivia.IsKind(SyntaxKind.IfDirectiveTrivia))
{
var directive = trivia.GetStructure() as ConditionalDirectiveTriviaSyntax;
if (directive != null && ContainsUnityEditorCondition(directive))
{
activeIfDirectiveStart = directive.SpanStart;
inEditorBlock = true;
}
}
else if (trivia.IsKind(SyntaxKind.EndIfDirectiveTrivia))
{
// Check if this #endif closes the active UNITY_EDITOR block
if (inEditorBlock && activeIfDirectiveStart >= 0)
{
// This #endif closes the UNITY_EDITOR block
// Check if node is before this #endif
if (nodeStart < trivia.SpanStart)
{
// Node is inside the UNITY_EDITOR block
return true;
}
// Node is after this #endif, so not in UNITY_EDITOR block
inEditorBlock = false;
activeIfDirectiveStart = -1;
}
}
else if (trivia.IsKind(SyntaxKind.ElseDirectiveTrivia))
{
// #else closes the if block, so if we're in UNITY_EDITOR block, it ends here
if (inEditorBlock && activeIfDirectiveStart >= 0)
{
if (nodeStart < trivia.SpanStart)
{
return true;
}
inEditorBlock = false;
activeIfDirectiveStart = -1;
}
}
}
// If we're still in an editor block and haven't hit an #endif, check if node is after the #if
if (inEditorBlock && activeIfDirectiveStart >= 0)
{
// Check if there's an #endif after the node
foreach (var trivia in allTrivia)
{
if (trivia.SpanStart <= nodeStart)
continue;
if (trivia.IsKind(SyntaxKind.EndIfDirectiveTrivia))
{
// Node is between #if UNITY_EDITOR and #endif
return true;
}
else if (trivia.IsKind(SyntaxKind.IfDirectiveTrivia))
{
// New #if starts, check if it's nested
var directive = trivia.GetStructure() as ConditionalDirectiveTriviaSyntax;
if (directive != null && ContainsUnityEditorCondition(directive))
{
// Nested UNITY_EDITOR block, continue checking
continue;
}
}
}
// If no #endif found after node, node is still in the block
return true;
}
return false;
}
private bool ContainsUnityEditorCondition(ConditionalDirectiveTriviaSyntax directive)
{
if (directive == null)
return false;
var condition = directive.Condition?.ToString();
if (string.IsNullOrEmpty(condition))
return false;
// Check for UNITY_EDITOR in the condition (exact match or as part of expression)
return condition.IndexOf("UNITY_EDITOR", StringComparison.OrdinalIgnoreCase) >= 0;
}
}
@@ -0,0 +1,320 @@
{
"runtimeTarget": {
"name": ".NETStandard,Version=v2.0/",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETStandard,Version=v2.0": {},
".NETStandard,Version=v2.0/": {
"UnityEditorOnlyAnalyzer/1.0.0": {
"dependencies": {
"Microsoft.CodeAnalysis.Analyzers": "3.3.4",
"Microsoft.CodeAnalysis.CSharp": "4.5.0",
"NETStandard.Library": "2.0.3"
},
"runtime": {
"UnityEditorOnlyAnalyzer.dll": {}
}
},
"Microsoft.CodeAnalysis.Analyzers/3.3.4": {},
"Microsoft.CodeAnalysis.Common/4.5.0": {
"dependencies": {
"Microsoft.CodeAnalysis.Analyzers": "3.3.4",
"System.Collections.Immutable": "6.0.0",
"System.Memory": "4.5.5",
"System.Reflection.Metadata": "6.0.1",
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
"System.Text.Encoding.CodePages": "6.0.0",
"System.Threading.Tasks.Extensions": "4.5.4"
},
"runtime": {
"lib/netstandard2.0/Microsoft.CodeAnalysis.dll": {
"assemblyVersion": "4.5.0.0",
"fileVersion": "4.500.23.10905"
}
},
"resources": {
"lib/netstandard2.0/cs/Microsoft.CodeAnalysis.resources.dll": {
"locale": "cs"
},
"lib/netstandard2.0/de/Microsoft.CodeAnalysis.resources.dll": {
"locale": "de"
},
"lib/netstandard2.0/es/Microsoft.CodeAnalysis.resources.dll": {
"locale": "es"
},
"lib/netstandard2.0/fr/Microsoft.CodeAnalysis.resources.dll": {
"locale": "fr"
},
"lib/netstandard2.0/it/Microsoft.CodeAnalysis.resources.dll": {
"locale": "it"
},
"lib/netstandard2.0/ja/Microsoft.CodeAnalysis.resources.dll": {
"locale": "ja"
},
"lib/netstandard2.0/ko/Microsoft.CodeAnalysis.resources.dll": {
"locale": "ko"
},
"lib/netstandard2.0/pl/Microsoft.CodeAnalysis.resources.dll": {
"locale": "pl"
},
"lib/netstandard2.0/pt-BR/Microsoft.CodeAnalysis.resources.dll": {
"locale": "pt-BR"
},
"lib/netstandard2.0/ru/Microsoft.CodeAnalysis.resources.dll": {
"locale": "ru"
},
"lib/netstandard2.0/tr/Microsoft.CodeAnalysis.resources.dll": {
"locale": "tr"
},
"lib/netstandard2.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll": {
"locale": "zh-Hans"
},
"lib/netstandard2.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.CodeAnalysis.CSharp/4.5.0": {
"dependencies": {
"Microsoft.CodeAnalysis.Common": "4.5.0"
},
"runtime": {
"lib/netstandard2.0/Microsoft.CodeAnalysis.CSharp.dll": {
"assemblyVersion": "4.5.0.0",
"fileVersion": "4.500.23.10905"
}
},
"resources": {
"lib/netstandard2.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "cs"
},
"lib/netstandard2.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "de"
},
"lib/netstandard2.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "es"
},
"lib/netstandard2.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "fr"
},
"lib/netstandard2.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "it"
},
"lib/netstandard2.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "ja"
},
"lib/netstandard2.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "ko"
},
"lib/netstandard2.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "pl"
},
"lib/netstandard2.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "pt-BR"
},
"lib/netstandard2.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "ru"
},
"lib/netstandard2.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "tr"
},
"lib/netstandard2.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "zh-Hans"
},
"lib/netstandard2.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.NETCore.Platforms/1.1.0": {},
"NETStandard.Library/2.0.3": {
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0"
}
},
"System.Buffers/4.5.1": {
"runtime": {
"lib/netstandard2.0/System.Buffers.dll": {
"assemblyVersion": "4.0.3.0",
"fileVersion": "4.6.28619.1"
}
}
},
"System.Collections.Immutable/6.0.0": {
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
},
"runtime": {
"lib/netstandard2.0/System.Collections.Immutable.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.Memory/4.5.5": {
"dependencies": {
"System.Buffers": "4.5.1",
"System.Numerics.Vectors": "4.4.0",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
},
"runtime": {
"lib/netstandard2.0/System.Memory.dll": {
"assemblyVersion": "4.0.1.2",
"fileVersion": "4.6.31308.1"
}
}
},
"System.Numerics.Vectors/4.4.0": {
"runtime": {
"lib/netstandard2.0/System.Numerics.Vectors.dll": {
"assemblyVersion": "4.1.3.0",
"fileVersion": "4.6.25519.3"
}
}
},
"System.Reflection.Metadata/6.0.1": {
"dependencies": {
"System.Collections.Immutable": "6.0.0"
},
"runtime": {
"lib/netstandard2.0/System.Reflection.Metadata.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.322.12309"
}
}
},
"System.Runtime.CompilerServices.Unsafe/6.0.0": {
"runtime": {
"lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.Text.Encoding.CodePages/6.0.0": {
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
},
"runtime": {
"lib/netstandard2.0/System.Text.Encoding.CodePages.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.Threading.Tasks.Extensions/4.5.4": {
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
},
"runtime": {
"lib/netstandard2.0/System.Threading.Tasks.Extensions.dll": {
"assemblyVersion": "4.2.0.1",
"fileVersion": "4.6.28619.1"
}
}
}
}
},
"libraries": {
"UnityEditorOnlyAnalyzer/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Microsoft.CodeAnalysis.Analyzers/3.3.4": {
"type": "package",
"serviceable": true,
"sha512": "sha512-AxkxcPR+rheX0SmvpLVIGLhOUXAKG56a64kV9VQZ4y9gR9ZmPXnqZvHJnmwLSwzrEP6junUF11vuc+aqo5r68g==",
"path": "microsoft.codeanalysis.analyzers/3.3.4",
"hashPath": "microsoft.codeanalysis.analyzers.3.3.4.nupkg.sha512"
},
"Microsoft.CodeAnalysis.Common/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==",
"path": "microsoft.codeanalysis.common/4.5.0",
"hashPath": "microsoft.codeanalysis.common.4.5.0.nupkg.sha512"
},
"Microsoft.CodeAnalysis.CSharp/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==",
"path": "microsoft.codeanalysis.csharp/4.5.0",
"hashPath": "microsoft.codeanalysis.csharp.4.5.0.nupkg.sha512"
},
"Microsoft.NETCore.Platforms/1.1.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==",
"path": "microsoft.netcore.platforms/1.1.0",
"hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512"
},
"NETStandard.Library/2.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
"path": "netstandard.library/2.0.3",
"hashPath": "netstandard.library.2.0.3.nupkg.sha512"
},
"System.Buffers/4.5.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==",
"path": "system.buffers/4.5.1",
"hashPath": "system.buffers.4.5.1.nupkg.sha512"
},
"System.Collections.Immutable/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==",
"path": "system.collections.immutable/6.0.0",
"hashPath": "system.collections.immutable.6.0.0.nupkg.sha512"
},
"System.Memory/4.5.5": {
"type": "package",
"serviceable": true,
"sha512": "sha512-XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
"path": "system.memory/4.5.5",
"hashPath": "system.memory.4.5.5.nupkg.sha512"
},
"System.Numerics.Vectors/4.4.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==",
"path": "system.numerics.vectors/4.4.0",
"hashPath": "system.numerics.vectors.4.4.0.nupkg.sha512"
},
"System.Reflection.Metadata/6.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==",
"path": "system.reflection.metadata/6.0.1",
"hashPath": "system.reflection.metadata.6.0.1.nupkg.sha512"
},
"System.Runtime.CompilerServices.Unsafe/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
"path": "system.runtime.compilerservices.unsafe/6.0.0",
"hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512"
},
"System.Text.Encoding.CodePages/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==",
"path": "system.text.encoding.codepages/6.0.0",
"hashPath": "system.text.encoding.codepages.6.0.0.nupkg.sha512"
},
"System.Threading.Tasks.Extensions/4.5.4": {
"type": "package",
"serviceable": true,
"sha512": "sha512-zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
"path": "system.threading.tasks.extensions/4.5.4",
"hashPath": "system.threading.tasks.extensions.4.5.4.nupkg.sha512"
}
}
}
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>UnityEditorOnlyAnalyzer</name>
</assembly>
<members>
</members>
</doc>
@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("UnityEditorOnlyAnalyzer")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3f57f648cedeea1d6fcdb384ee3b716fb9ec6b38")]
[assembly: System.Reflection.AssemblyProductAttribute("UnityEditorOnlyAnalyzer")]
[assembly: System.Reflection.AssemblyTitleAttribute("UnityEditorOnlyAnalyzer")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Generated by the MSBuild WriteCodeFragment class.
@@ -0,0 +1 @@
e6393a12ef7d750e9cde3f8543417fb1572de567f7aa7467f6fa38578092253c
@@ -0,0 +1,16 @@
is_global = true
build_property.TargetFramework = netstandard2.0
build_property.TargetPlatformMinVersion = 7.0
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules = true
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = UnityEditorOnlyAnalyzer
build_property.ProjectDir = E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.CsWinRTUseWindowsUIXamlProjections = false
build_property.EffectiveAnalysisLevelStyle =
build_property.EnableCodeStyleSeverity =
@@ -0,0 +1 @@
3fc34b92f89c75b4779e8740553e8ef89532076d6c3fabf38ce6b74c9cbc2bc1
@@ -0,0 +1,12 @@
E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\obj\Release\netstandard2.0\UnityEditorOnlyAnalyzer.csproj.AssemblyReference.cache
E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\obj\Release\netstandard2.0\UnityEditorOnlyAnalyzer.GeneratedMSBuildEditorConfig.editorconfig
E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\obj\Release\netstandard2.0\UnityEditorOnlyAnalyzer.AssemblyInfoInputs.cache
E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\obj\Release\netstandard2.0\UnityEditorOnlyAnalyzer.AssemblyInfo.cs
E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\obj\Release\netstandard2.0\UnityEditorOnlyAnalyzer.csproj.CoreCompileInputs.cache
E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.deps.json
E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll
E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.pdb
E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\bin\Release\netstandard2.0\UnityEditorOnlyAnalyzer.xml
E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\obj\Release\netstandard2.0\UnityEditorOnlyAnalyzer.dll
E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\obj\Release\netstandard2.0\UnityEditorOnlyAnalyzer.xml
E:\Projects\perfect-world-unity\UnityEditorOnlyAnalyzer\obj\Release\netstandard2.0\UnityEditorOnlyAnalyzer.pdb
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>UnityEditorOnlyAnalyzer</name>
</assembly>
<members>
</members>
</doc>
@@ -0,0 +1,86 @@
{
"format": 1,
"restore": {
"E:\\Projects\\perfect-world-unity\\UnityEditorOnlyAnalyzer\\UnityEditorOnlyAnalyzer.csproj": {}
},
"projects": {
"E:\\Projects\\perfect-world-unity\\UnityEditorOnlyAnalyzer\\UnityEditorOnlyAnalyzer.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "E:\\Projects\\perfect-world-unity\\UnityEditorOnlyAnalyzer\\UnityEditorOnlyAnalyzer.csproj",
"projectName": "UnityEditorOnlyAnalyzer",
"projectPath": "E:\\Projects\\perfect-world-unity\\UnityEditorOnlyAnalyzer\\UnityEditorOnlyAnalyzer.csproj",
"packagesPath": "C:\\Users\\BrewPC\\.nuget\\packages\\",
"outputPath": "E:\\Projects\\perfect-world-unity\\UnityEditorOnlyAnalyzer\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"E:\\VSShare\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\BrewPC\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"netstandard2.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"netstandard2.0": {
"targetAlias": "netstandard2.0",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.300"
},
"frameworks": {
"netstandard2.0": {
"targetAlias": "netstandard2.0",
"dependencies": {
"Microsoft.CodeAnalysis.Analyzers": {
"suppressParent": "All",
"target": "Package",
"version": "[3.3.4, )"
},
"Microsoft.CodeAnalysis.CSharp": {
"suppressParent": "All",
"target": "Package",
"version": "[4.5.0, )"
},
"NETStandard.Library": {
"suppressParent": "All",
"target": "Package",
"version": "[2.0.3, )",
"autoReferenced": true
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.306\\RuntimeIdentifierGraph.json"
}
}
}
}
}
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\BrewPC\.nuget\packages\;E:\VSShare\NuGetPackages</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\BrewPC\.nuget\packages\" />
<SourceRoot Include="E:\VSShare\NuGetPackages\" />
</ItemGroup>
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)microsoft.codeanalysis.analyzers\3.3.4\buildTransitive\Microsoft.CodeAnalysis.Analyzers.props" Condition="Exists('$(NuGetPackageRoot)microsoft.codeanalysis.analyzers\3.3.4\buildTransitive\Microsoft.CodeAnalysis.Analyzers.props')" />
</ImportGroup>
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<PkgMicrosoft_CodeAnalysis_Analyzers Condition=" '$(PkgMicrosoft_CodeAnalysis_Analyzers)' == '' ">C:\Users\BrewPC\.nuget\packages\microsoft.codeanalysis.analyzers\3.3.4</PkgMicrosoft_CodeAnalysis_Analyzers>
</PropertyGroup>
</Project>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)netstandard.library\2.0.3\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('$(NuGetPackageRoot)netstandard.library\2.0.3\build\netstandard2.0\NETStandard.Library.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.codeanalysis.analyzers\3.3.4\buildTransitive\Microsoft.CodeAnalysis.Analyzers.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.codeanalysis.analyzers\3.3.4\buildTransitive\Microsoft.CodeAnalysis.Analyzers.targets')" />
</ImportGroup>
</Project>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,22 @@
{
"version": 2,
"dgSpecHash": "E83ZNZ9Iojc=",
"success": true,
"projectFilePath": "E:\\Projects\\perfect-world-unity\\UnityEditorOnlyAnalyzer\\UnityEditorOnlyAnalyzer.csproj",
"expectedPackageFiles": [
"C:\\Users\\BrewPC\\.nuget\\packages\\microsoft.codeanalysis.analyzers\\3.3.4\\microsoft.codeanalysis.analyzers.3.3.4.nupkg.sha512",
"C:\\Users\\BrewPC\\.nuget\\packages\\microsoft.codeanalysis.common\\4.5.0\\microsoft.codeanalysis.common.4.5.0.nupkg.sha512",
"C:\\Users\\BrewPC\\.nuget\\packages\\microsoft.codeanalysis.csharp\\4.5.0\\microsoft.codeanalysis.csharp.4.5.0.nupkg.sha512",
"C:\\Users\\BrewPC\\.nuget\\packages\\microsoft.netcore.platforms\\1.1.0\\microsoft.netcore.platforms.1.1.0.nupkg.sha512",
"C:\\Users\\BrewPC\\.nuget\\packages\\netstandard.library\\2.0.3\\netstandard.library.2.0.3.nupkg.sha512",
"C:\\Users\\BrewPC\\.nuget\\packages\\system.buffers\\4.5.1\\system.buffers.4.5.1.nupkg.sha512",
"C:\\Users\\BrewPC\\.nuget\\packages\\system.collections.immutable\\6.0.0\\system.collections.immutable.6.0.0.nupkg.sha512",
"C:\\Users\\BrewPC\\.nuget\\packages\\system.memory\\4.5.5\\system.memory.4.5.5.nupkg.sha512",
"C:\\Users\\BrewPC\\.nuget\\packages\\system.numerics.vectors\\4.4.0\\system.numerics.vectors.4.4.0.nupkg.sha512",
"C:\\Users\\BrewPC\\.nuget\\packages\\system.reflection.metadata\\6.0.1\\system.reflection.metadata.6.0.1.nupkg.sha512",
"C:\\Users\\BrewPC\\.nuget\\packages\\system.runtime.compilerservices.unsafe\\6.0.0\\system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512",
"C:\\Users\\BrewPC\\.nuget\\packages\\system.text.encoding.codepages\\6.0.0\\system.text.encoding.codepages.6.0.0.nupkg.sha512",
"C:\\Users\\BrewPC\\.nuget\\packages\\system.threading.tasks.extensions\\4.5.4\\system.threading.tasks.extensions.4.5.4.nupkg.sha512"
],
"logs": []
}
+52
View File
@@ -0,0 +1,52 @@
# Nguyên nhân analyzer vẫn màu xanh
Tìm ra nguyên nhân: File `.editorconfig` đang override severity về `warning`!
## Vấn đề
File `.editorconfig` (dòng 16) có:
```ini
dotnet_diagnostic.UNITY_EDITOR_ONLY_USAGE.severity = warning
```
EditorConfig có **priority cao hơn** analyzer code, nên dù analyzer code đã đổi thành `Error`, EditorConfig vẫn override về `warning`.
## Đã sửa
Đổi dòng 16 trong `.editorconfig` từ:
```ini
dotnet_diagnostic.UNITY_EDITOR_ONLY_USAGE.severity = warning
```
Thành:
```ini
dotnet_diagnostic.UNITY_EDITOR_ONLY_USAGE.severity = error
```
## Reload Visual Studio
1. Đóng Visual Studio hoàn toàn
2. Mở lại: double-click `perfect-world-unity.sln`
3. Visual Studio sẽ đọc `.editorconfig` mới
4. `GetCurPanel1()` sẽ hiển thị màu đỏ
## Priority Order
```
EditorConfig (.editorconfig)
↓ (highest priority)
Project Settings (.csproj)
Analyzer Code (DiagnosticDescriptor)
↓ (lowest priority)
```
EditorConfig luôn thắng!
## Kết luận
Đã sửa cả 2 chỗ:
- ✅ Analyzer code: `DiagnosticSeverity.Error`
- ✅ EditorConfig: `severity = error`
Reload Visual Studio để thấy màu đỏ.
+79
View File
@@ -0,0 +1,79 @@
# Tóm tắt Dự án (Project Context)
- Mục tiêu: Chuyển đổi dự án Perfect World từ bản gốc PC (`e:\Projects\perfect-world-source`) sang phiên bản Mobile sử dụng Unity (`e:\Projects\perfect-world-unity`).
- Ngôn ngữ & Nền tảng đích: C#, Unity Engine.
# Nguyên Tắc Hoạt Động Của AI (Core Rules)
## 1. BẮT BUỘC LÀM RÕ YÊU CẦU (Clarification First Principle)
- TUYỆT ĐỐI KHÔNG tự ý suy đoán và bắt đầu thay đổi code (hoặc thực hiện bất kỳ workflow nào) khi yêu cầu của người dùng còn mơ hồ, thiếu chi tiết hoặc có thể hiểu theo nhiều nghĩa.
- NGUYÊN TẮC CỐT LÕI: "Liên tục hỏi và xác nhận cho đến khi chắc chắn 100% mới bắt đầu thực thi".
## 2. Quy trình 5 bước tiếp nhận và xử lý yêu cầu:
- **Bước 1 - Phân tích:** Tiếp nhận yêu cầu. Đối chiếu với bối cảnh chuyển đổi từ PC sang Mobile.
- **Bước 2 - Tìm điểm mù:** Tự hỏi bản thân xem có phần nào trong yêu cầu chưa đủ thông tin để viết code lập tức không (vd: thiếu file tham chiếu, logic mobile khác pc chỗ nào, biến nào chịu trách nhiệm lưu trữ trạng thái...).
- **Bước 3 - Đặt câu hỏi:** Đưa ra các câu hỏi ngắn gọn, liệt kê theo gạch đầu dòng để người dùng giải đáp. (VD: "Ý bạn là nút này trên mobile chạm vào sẽ hiện ra mảng 15 skill hay sao?")
- **Bước 4 - Chốt phương án:** Sau khi người dùng đã giải đáp xong mọi thắc mắc, tóm tắt lại phương án giải quyết (Kế hoạch thực hiện) và chờ người dùng nói "OK/Đồng ý".
- **Bước 5 - Thực thi:** Chỉ tiến hành chạy công cụ sửa đổi code hoặc tạo file mới khi bước 4 đã hoàn tất.
## 3. Lưu ý đặc thù về chuyển đổi PC -> Mobile
- Bất cứ khi nào làm việc với UI hoặc Input, luôn nhớ Mobile dùng Touch/Drag thay vì Mouse Click/Hover.
- Nếu gặp logic không rõ ràng trong `perfect-world-unity`, hãy yêu cầu người dùng trỏ tới file tương ứng ở `perfect-world-source` để tham khảo cách hoạt động gốc.
- **Quy tắc đặt tên khi convert:** Ưu tiên không thay đổi tên biến và hàm nếu có thể, làm giống bên C++ nhất có thể để dễ dàng đối chiếu và debug.
## 4. Workflow Init Dự Án (Project Initialization)
- **Khi nào:** Khi người dùng yêu cầu init dự án hoặc khi bắt đầu làm việc với dự án mới.
- **Mục đích:** Thu thập và ghi lại thông tin tổng quan về dự án vào file `claude.md` để làm context cho các phiên làm việc sau.
- **Quy trình thực hiện:**
1. **Quét cấu trúc thư mục:** Sử dụng `list_dir` để khám phá cấu trúc thư mục chính của dự án (Assets, Scripts, Resources, v.v.).
2. **Xác định module chính:** Tìm và liệt kê các module/tính năng chính của dự án (Skill System, Network, UI, Movement, v.v.).
3. **Phân tích file quan trọng:** Đọc các file quan trọng như README, config files, entry points để hiểu kiến trúc tổng thể.
4. **Xác định pattern/convention:** Phân tích naming convention, coding style, design patterns được sử dụng trong dự án.
5. **Ghi vào claude.md:** Cập nhật file `claude.md` với các thông tin:
- Cấu trúc thư mục chính
- Danh sách module/tính năng quan trọng
- File entry points và file quan trọng
- Kiến trúc hệ thống (nếu có)
- Pattern và convention được sử dụng
- Thông tin về dependencies và build system
- **Lưu ý:** Không cần đọc toàn bộ code, chỉ tập trung vào cấu trúc tổng thể và các file quan trọng để có cái nhìn tổng quan.
## 5. Chế Độ Ghi Nhớ (Memory Mode)
- **Kích hoạt:** Khi người dùng bắt đầu câu lệnh bằng ký tự `#` (ví dụ: `# Rule mới: ...`, `# Ghi nhớ: ...`, `# Lưu ý: ...`) hoặc lệnh `/memory`.
- **Hành động bắt buộc:**
- AI phải NGAY LẬP TỨC nhận diện đây là Memory Mode.
- Phân tích và trích xuất thông tin cần ghi nhớ từ câu lệnh của người dùng.
- Tự động cập nhật nội dung đó vào file `claude.md` ở section phù hợp (hoặc tạo section mới nếu cần).
- Không cần hỏi lại người dùng, chỉ cần ghi nhớ và cập nhật.
- **Sau khi ghi:** Báo cáo ngắn gọn cho người dùng là đã cập nhật luật/ghi nhớ thành công vào `claude.md`.
- **Mục đích:** Tích lũy kiến thức và quy tắc từ các phiên làm việc để sử dụng cho các lần trò chuyện hiện tại và sau này.
## 6. Chiến Lược Hiểu Codebase (Codebase Exploration)
- **Giới hạn thực tế:** Dự án MMO (C++ & Unity) có quy mô khổng lồ (hàng ngàn file). Trí tuệ nhân tạo không thể "đọc" toàn bộ source code cùng một lúc do giới hạn bộ nhớ khả dụng trong một phiên hội thoại (context window).
- **Phương pháp "Cuốn chiếu" (On-demand Analysis):**
- **Truy xuất mục tiêu:** Khi bắt tay vào làm một *tính năng cụ thể* (VD: Skill, QuickBar, Di chuyển, Network), AI sẽ dùng công cụ quét (grep/search) để đọc chính xác các file liên quan ở bản C++ gốc và đối chiếu với bản C#.
- **Tích lũy kiến thức:** Xử lý xong phần nào, AI sẽ đúc kết luồng logic của phần đó và ghi chú lại vào hệ thống Knowledge hoặc file `claude.md` để ghi nhớ vĩnh viễn.
- **Cấu trúc Mặc định:**
- **Bản gốc (C++)**: Nằm tại `~\CElement`. Logic chính của game client nằm trong `CElementClient`; Hệ thống UI (như bảng skill, túi đồ) nằm ở `AUInterface2` / `CElementClient`; Logic về hiệu ứng/đồ họa ở `GfxCommon`.
- **Bản Unity (C#)**: Mọi sự chỉnh sửa, code logic chuyển đổi đều được tiến hành và lưu ở `~\perfect-world-unity\Assets`.
## 7. Phát Hiện Lỗi Build Tiềm Ẩn (Build Error Detection)
- **Khi nào:** Khi code hoặc scan code trong quá trình thực thi yêu cầu của người dùng.
- **Mục đích:** Tự động phát hiện và cảnh báo các lỗi build tiềm ẩn trước khi chúng gây ra vấn đề trong quá trình build.
- **Các loại lỗi cần phát hiện:**
1. **Conditional Compilation Mismatch:**
- Phát hiện khi một method/class được định nghĩa trong `#if UNITY_EDITOR` (hoặc các directive khác) nhưng được gọi từ code không có cùng directive.
- Ví dụ:
```csharp
#if UNITY_EDITOR
public void MethodA(){}
#endif
public void MethodB(){ MethodA(); } // ❌ Lỗi: MethodA không tồn tại khi build không phải Editor
```
- Giải pháp: Đảm bảo code gọi method cũng nằm trong cùng directive, hoặc sử dụng `#if UNITY_EDITOR` cho cả MethodB, hoặc tách logic để tránh dependency.
2. **Missing References:** Kiểm tra các reference đến class/method có thể không tồn tại trong một số build configuration.
3. **Platform-Specific Code:** Phát hiện code chỉ hoạt động trên một platform cụ thể nhưng được gọi từ code chung.
- **Hành động khi phát hiện:**
- Cảnh báo người dùng ngay lập tức về lỗi tiềm ẩn.
- Đề xuất giải pháp khắc phục cụ thể.
- Không tự động sửa mà chờ xác nhận từ người dùng (theo quy trình 5 bước).
- **Lưu ý:** Luôn kiểm tra các directive như `#if UNITY_EDITOR`, `#if UNITY_STANDALONE`, `#if UNITY_ANDROID`, `#if UNITY_IOS`, v.v. khi scan code.