8 Commits
13 changed files with 447 additions and 15 deletions
+27 -9
View File
@@ -15,22 +15,39 @@ jobs:
env: env:
NODE_AUTH_TOKEN: ${{ secrets.SHRINKSDK_PACKAGE_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.SHRINKSDK_PACKAGE_TOKEN }}
steps: steps:
- name: Fetch tagged revision - name: Fetch exact tagged release archive
env:
GITEA_REF: ${{ gitea.ref }}
shell: bash shell: bash
run: | run: |
set -eu set -eu
ref="${{ gitea.sha }}" tag="${GITEA_REF#refs/tags/}"
test -n "$ref" case "$tag" in
git init . v[0-9]*) ;;
git remote add origin "https://git.crash.work/ShrinkSDK/ShrinkEventBus.git" *) echo "Expected a version tag ref, got: $GITEA_REF" >&2; exit 1 ;;
git fetch --depth=1 origin "$ref" esac
git checkout --detach FETCH_HEAD export SHRINKSDK_ARCHIVE_URL="https://git.crash.work/ShrinkSDK/ShrinkEventBus/archive/${tag}.tar.gz"
node --input-type=module <<'NODE'
import { writeFile } from 'node:fs/promises';
const response = await fetch(process.env.SHRINKSDK_ARCHIVE_URL);
if (!response.ok) {
throw new Error(`Release archive download failed: ${response.status} ${response.statusText}`);
}
await writeFile('release.tar.gz', new Uint8Array(await response.arrayBuffer()));
NODE
mkdir release
tar -xzf release.tar.gz --strip-components=1 -C release
rm -f release.tar.gz
printf '%s' "$tag" > release/.shrink-sdk-release-tag
- name: Validate immutable release version - name: Validate immutable release version
shell: bash shell: bash
run: | run: |
set -eu set -eu
tag="$(git describe --exact-match --tags HEAD)" cd release
tag="$(cat .shrink-sdk-release-tag)"
version="$(node -p "require('./package.json').version")" version="$(node -p "require('./package.json').version")"
test "$tag" = "v$version" test "$tag" = "v$version"
npm pack --dry-run npm pack --dry-run
@@ -40,10 +57,11 @@ jobs:
run: | run: |
set -eu set -eu
: "${NODE_AUTH_TOKEN:?SHRINKSDK_PACKAGE_TOKEN is required}" : "${NODE_AUTH_TOKEN:?SHRINKSDK_PACKAGE_TOKEN is required}"
cd release
npmrc="$HOME/.npmrc" npmrc="$HOME/.npmrc"
cleanup() { rm -f "$npmrc"; } cleanup() { rm -f "$npmrc"; }
trap cleanup EXIT trap cleanup EXIT
printf '%s\n' \ printf '%s\n' \
'registry=https://git.crash.work/api/packages/ShrinkSDK/npm/' \ 'registry=https://git.crash.work/api/packages/ShrinkSDK/npm/' \
'//git.crash.work/api/packages/ShrinkSDK/npm/:_authToken=${NODE_AUTH_TOKEN}' > "$npmrc" '//git.crash.work/api/packages/ShrinkSDK/npm/:_authToken=${NODE_AUTH_TOKEN}' > "$npmrc"
npm publish --registry=https://git.crash.work/api/packages/ShrinkSDK/npm/ npm publish --registry=https://git.crash.work/api/packages/ShrinkSDK/npm/
+6 -1
View File
@@ -26,12 +26,17 @@ jobs:
shell: bash shell: bash
run: | run: |
set -eu set -eu
machine_id_file="/root/.local/share/unity3d/Unity/.machine-id"
if test -s "$machine_id_file"; then
cat "$machine_id_file" > /etc/machine-id
echo "Unity machine identity restored"
fi
git config --global url."https://ghfast.top/https://github.com/".insteadOf "https://github.com/"
unity_bin="$(command -v unity-editor || command -v unity || command -v Unity || true)" unity_bin="$(command -v unity-editor || command -v unity || command -v Unity || true)"
test -n "$unity_bin" test -n "$unity_bin"
"$unity_bin" \ "$unity_bin" \
-batchmode \ -batchmode \
-nographics \ -nographics \
-quit \
-projectPath "$PWD/Development~/UnityProject" \ -projectPath "$PWD/Development~/UnityProject" \
-runTests \ -runTests \
-testPlatform EditMode \ -testPlatform EditMode \
+1
View File
@@ -41,6 +41,7 @@
ExportedObj/ ExportedObj/
.consulo/ .consulo/
*.csproj *.csproj
!Tools~/DotNet/**/*.csproj
*.unityproj *.unityproj
*.sln *.sln
*.suo *.suo
+7
View File
@@ -2,6 +2,13 @@
本文件记录 `ShrinkEventBus` 在当前工作区中的包内变更。 本文件记录 `ShrinkEventBus` 在当前工作区中的包内变更。
## [2.0.1] - 2026-08-28
### Added
- `ShrinkEventSubscriberAttribute` 新增显式 `Lifetime`,选择 `AwakeToDestroy``MonoBehaviour` 会由 ILPostProcessor 在编译期织入生成绑定的 `Attach``Dispose`,禁用对象时保持订阅,销毁时自动释放。
- 自动生命周期支持已有或缺失的 `Awake` / `OnDestroy`,并保留可重写基类生命周期调用;默认仍为 `Manual`,避免既有 2.0 使用方升级后重复订阅。
## [2.0.0] - 2026-08-23 ## [2.0.0] - 2026-08-23
### Added ### Added
+252 -1
View File
@@ -43,12 +43,25 @@ namespace ShrinkEventBus.CodeGen
return GetResult(assemblyDefinition, diagnostics); return GetResult(assemblyDefinition, diagnostics);
foreach (var type in GetAllTypes(module.Types) foreach (var type in GetAllTypes(module.Types)
.Where(type => !type.IsInterface && !type.IsAbstract) .Where(type => !type.IsInterface)
.Where(type => HasAttribute(type, generatedSubscriberType)) .Where(type => HasAttribute(type, generatedSubscriberType))
.Where(type => type.Methods.Any(method => .Where(type => type.Methods.Any(method =>
!method.IsStatic && HasAttribute(method, generatedSubscribeType)))) !method.IsStatic && HasAttribute(method, generatedSubscribeType))))
{ {
var subscriberAttribute = type.CustomAttributes.First(attribute =>
attribute.AttributeType.FullName == generatedSubscriberType.FullName);
InjectGeneratedBinding(type, module, generatedSubscribeType); InjectGeneratedBinding(type, module, generatedSubscribeType);
switch (ReadIntProperty(subscriberAttribute, "Lifetime", 0))
{
case 0:
break;
case 1:
InjectAwakeToDestroyLifetime(type, module);
break;
default:
throw new InvalidOperationException(
$"Unsupported ShrinkSubscriberLifetime on {type.FullName}.");
}
} }
var staticSubscriberTypes = GetAllTypes(module.Types) var staticSubscriberTypes = GetAllTypes(module.Types)
@@ -176,6 +189,244 @@ namespace ShrinkEventBus.CodeGen
type.Methods.Add(generatedMethod); type.Methods.Add(generatedMethod);
} }
private static void InjectAwakeToDestroyLifetime(TypeDefinition type, ModuleDefinition module)
{
if (!InheritsFrom(type, "UnityEngine.MonoBehaviour"))
{
throw new InvalidOperationException(
$"[ShrinkEventSubscriber(Lifetime = AwakeToDestroy)] requires MonoBehaviour: {type.FullName}.");
}
const string bindingFieldName = "__shrinkEventBusAwakeToDestroyBinding";
if (type.Fields.Any(field => field.Name == bindingFieldName))
{
throw new InvalidOperationException(
$"Reserved generated field already exists on {type.FullName}: {bindingFieldName}.");
}
var disposableType = module.ImportReference(typeof(IDisposable));
var bindingField = new FieldDefinition(bindingFieldName,
FieldAttributes.Private, disposableType);
type.Fields.Add(bindingField);
var eventBusType = FindType(module, "ShrinkEventBus.EventBus", RuntimeAssemblyName)
?? throw new InvalidOperationException("EventBus was not found.");
var eventBusDefinition = eventBusType.Resolve()
?? throw new InvalidOperationException("EventBus could not be resolved.");
var attachMethod = module.ImportReference(eventBusDefinition.Methods.Single(method =>
method.Name == "Attach" && method.IsStatic && method.Parameters.Count == 2));
var disposeMethod = module.ImportReference(typeof(IDisposable).GetMethod(nameof(IDisposable.Dispose))
?? throw new InvalidOperationException("IDisposable.Dispose was not found."));
InjectAwake(type, module, bindingField, attachMethod);
InjectOnDestroy(type, module, bindingField, disposeMethod);
}
private static void InjectAwake(TypeDefinition type, ModuleDefinition module,
FieldDefinition bindingField, MethodReference attachMethod)
{
var awake = type.Methods.FirstOrDefault(method =>
method.Name == "Awake" && !method.IsStatic && method.Parameters.Count == 0);
if (awake != null)
{
InsertAttachAtStart(awake, module, bindingField, attachMethod);
return;
}
var baseAwake = FindBaseMethodReference(type, "Awake", module);
awake = new MethodDefinition("Awake",
baseAwake != null
? MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.Virtual
: MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.Virtual |
MethodAttributes.NewSlot,
module.TypeSystem.Void);
awake.Body.InitLocals = true;
var il = awake.Body.GetILProcessor();
if (baseAwake != null)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, baseAwake);
}
EmitAttach(il, awake, module, bindingField, attachMethod);
il.Emit(OpCodes.Ret);
type.Methods.Add(awake);
}
private static void InjectOnDestroy(TypeDefinition type, ModuleDefinition module,
FieldDefinition bindingField, MethodReference disposeMethod)
{
var onDestroy = type.Methods.FirstOrDefault(method =>
method.Name == "OnDestroy" && !method.IsStatic && method.Parameters.Count == 0);
if (onDestroy != null)
{
InsertDisposeAtStart(onDestroy, bindingField, disposeMethod);
return;
}
var baseOnDestroy = FindBaseMethodReference(type, "OnDestroy", module);
onDestroy = new MethodDefinition("OnDestroy",
baseOnDestroy != null
? MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.Virtual
: MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.Virtual |
MethodAttributes.NewSlot,
module.TypeSystem.Void);
var il = onDestroy.Body.GetILProcessor();
EmitDispose(il, bindingField, disposeMethod);
if (baseOnDestroy != null)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, baseOnDestroy);
}
il.Emit(OpCodes.Ret);
type.Methods.Add(onDestroy);
}
private static void InsertAttachAtStart(MethodDefinition method, ModuleDefinition module,
FieldDefinition bindingField, MethodReference attachMethod)
{
if (!method.HasBody || method.Body.Instructions.Count == 0)
throw new InvalidOperationException($"Awake has no body: {method.FullName}.");
method.Body.InitLocals = true;
var processor = method.Body.GetILProcessor();
var instructions = BuildAttachInstructions(processor, method, module, bindingField, attachMethod);
var first = method.Body.Instructions[0];
foreach (var instruction in instructions)
processor.InsertBefore(first, instruction);
}
private static void EmitAttach(ILProcessor il, MethodDefinition method, ModuleDefinition module,
FieldDefinition bindingField, MethodReference attachMethod)
{
foreach (var instruction in BuildAttachInstructions(il, method, module, bindingField, attachMethod))
il.Append(instruction);
}
private static IReadOnlyList<Instruction> BuildAttachInstructions(ILProcessor il,
MethodDefinition method, ModuleDefinition module, FieldDefinition bindingField,
MethodReference attachMethod)
{
var defaultBusType = module.ImportReference(attachMethod.Parameters[1].ParameterType);
var defaultBus = new VariableDefinition(defaultBusType);
method.Body.Variables.Add(defaultBus);
var attached = il.Create(OpCodes.Nop);
return new[]
{
il.Create(OpCodes.Ldarg_0),
il.Create(OpCodes.Ldfld, bindingField),
il.Create(OpCodes.Brtrue_S, attached),
il.Create(OpCodes.Ldarg_0),
il.Create(OpCodes.Ldarg_0),
il.Create(OpCodes.Ldloca_S, defaultBus),
il.Create(OpCodes.Initobj, defaultBusType),
il.Create(OpCodes.Ldloc, defaultBus),
il.Create(OpCodes.Call, attachMethod),
il.Create(OpCodes.Stfld, bindingField),
attached
};
}
private static void InsertDisposeAtStart(MethodDefinition method,
FieldDefinition bindingField, MethodReference disposeMethod)
{
if (!method.HasBody || method.Body.Instructions.Count == 0)
throw new InvalidOperationException($"OnDestroy has no body: {method.FullName}.");
var processor = method.Body.GetILProcessor();
var instructions = BuildDisposeInstructions(processor, bindingField, disposeMethod);
var first = method.Body.Instructions[0];
foreach (var instruction in instructions)
processor.InsertBefore(first, instruction);
}
private static void EmitDispose(ILProcessor il, FieldDefinition bindingField,
MethodReference disposeMethod)
{
foreach (var instruction in BuildDisposeInstructions(il, bindingField, disposeMethod))
il.Append(instruction);
}
private static IReadOnlyList<Instruction> BuildDisposeInstructions(ILProcessor il,
FieldDefinition bindingField, MethodReference disposeMethod)
{
var disposed = il.Create(OpCodes.Nop);
return new[]
{
il.Create(OpCodes.Ldarg_0),
il.Create(OpCodes.Ldfld, bindingField),
il.Create(OpCodes.Brfalse_S, disposed),
il.Create(OpCodes.Ldarg_0),
il.Create(OpCodes.Ldfld, bindingField),
il.Create(OpCodes.Callvirt, disposeMethod),
il.Create(OpCodes.Ldarg_0),
il.Create(OpCodes.Ldnull),
il.Create(OpCodes.Stfld, bindingField),
disposed
};
}
private static MethodReference? FindBaseMethodReference(TypeDefinition type,
string methodName, ModuleDefinition module)
{
try
{
var baseTypeReference = type.BaseType;
while (baseTypeReference != null)
{
var baseType = baseTypeReference.Resolve();
if (baseType == null)
break;
var method = baseType.Methods.FirstOrDefault(candidate =>
candidate.Name == methodName && !candidate.IsStatic && candidate.IsVirtual &&
candidate.Parameters.Count == 0);
if (method != null)
{
if (baseTypeReference is GenericInstanceType genericBase)
{
var methodReference = new MethodReference(method.Name,
module.ImportReference(method.ReturnType), module.ImportReference(genericBase))
{
HasThis = method.HasThis,
ExplicitThis = method.ExplicitThis,
CallingConvention = method.CallingConvention
};
return methodReference;
}
return module.ImportReference(method);
}
baseTypeReference = baseType.BaseType;
}
}
catch
{
}
return null;
}
private static bool InheritsFrom(TypeDefinition type, string expectedFullName)
{
var current = type.BaseType;
while (current != null)
{
if (current.FullName == expectedFullName)
return true;
try
{
current = current.Resolve()?.BaseType;
}
catch
{
return false;
}
}
return false;
}
private static void EmitGeneratedSubscription(ILProcessor il, ModuleDefinition module, private static void EmitGeneratedSubscription(ILProcessor il, ModuleDefinition module,
TypeDefinition ownerType, MethodDefinition handler, CustomAttribute attribute, TypeDefinition ownerType, MethodDefinition handler, CustomAttribute attribute,
string classDefaultBus, VariableDefinition bindingLocal, MethodReference bindingAdd, string classDefaultBus, VariableDefinition bindingLocal, MethodReference bindingAdd,
@@ -10,6 +10,10 @@
], ],
"dependencies": { "dependencies": {
"com.unity.test-framework": "1.1.33", "com.unity.test-framework": "1.1.33",
"com.cneicy.shrink-eventbus": "file:../../.." "com.cneicy.shrink-eventbus": "file:../../..",
} "com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#7c0f199fe0d3fc528024488ccd671e6c7b27745b"
},
"testables": [
"com.cneicy.shrink-eventbus"
]
} }
+12 -1
View File
@@ -80,7 +80,18 @@ Bus 解析顺序:方法 `Bus`、类型 `DefaultBus`、`Attach` 传入默认 Bu
using var binding = EventBus.Attach(new PlayerHandlers()); using var binding = EventBus.Attach(new PlayerHandlers());
``` ```
MonoBehaviour 不需要继承 SDK 基类。可自行在 `OnEnable/OnDisable` 中 Attach/Dispose,也可以在 GameObject 上添加 `ShrinkMonoEventScope`,由它统一绑定同对象或子层级中的生成 subscriber。 MonoBehaviour 不需要继承 SDK 基类。需要从 `Awake` 持续订阅到 `OnDestroy` 时,可以显式选择编译期生命周期织入:
```csharp
[ShrinkEventSubscriber(Lifetime = ShrinkSubscriberLifetime.AwakeToDestroy)]
public sealed class PlayerHandlers : MonoBehaviour
{
[ShrinkSubscribe]
private void OnJoined(PlayerJoinedEvent value) { }
}
```
默认 `Lifetime``Manual`,普通对象与由 Context、Scene 或 Mod 宿主管理的实例继续自行持有 `EventBus.Attach(...)` 返回的绑定。需要随启用状态反复订阅的 MonoBehaviour 可以自行在 `OnEnable/OnDisable` 中 Attach/Dispose,也可以在 GameObject 上添加 `ShrinkMonoEventScope`,由它统一绑定同对象或子层级中的生成 subscriber。
静态类型同样只使用特性: 静态类型同样只使用特性:
+7
View File
@@ -2,11 +2,18 @@ using System;
namespace ShrinkEventBus namespace ShrinkEventBus
{ {
public enum ShrinkSubscriberLifetime
{
Manual = 0,
AwakeToDestroy = 1
}
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class ShrinkEventSubscriberAttribute : Attribute public sealed class ShrinkEventSubscriberAttribute : Attribute
{ {
public string OwnerId { get; set; } = string.Empty; public string OwnerId { get; set; } = string.Empty;
public string DefaultBus { get; set; } = string.Empty; public string DefaultBus { get; set; } = string.Empty;
public ShrinkSubscriberLifetime Lifetime { get; set; } = ShrinkSubscriberLifetime.Manual;
} }
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
@@ -14,6 +14,41 @@ namespace ShrinkEventBus.PlayMode.Tests
public int Value { get; } public int Value { get; }
} }
internal readonly struct AwakeToDestroyEvent : IShrinkEvent
{
public AwakeToDestroyEvent(int value) => Value = value;
public int Value { get; }
}
[ShrinkEventSubscriber(
DefaultBus = "game",
Lifetime = ShrinkSubscriberLifetime.AwakeToDestroy)]
internal sealed class AwakeToDestroyTarget : MonoBehaviour
{
public static int Sum { get; set; }
public static int AwakeCalls { get; set; }
public static int DestroyCalls { get; set; }
private void Awake() => AwakeCalls++;
private void OnDestroy() => DestroyCalls++;
[ShrinkSubscribe]
private void OnEvent(AwakeToDestroyEvent value) => Sum += value.Value;
}
[ShrinkEventSubscriber(Lifetime = ShrinkSubscriberLifetime.AwakeToDestroy)]
internal abstract class AbstractAwakeToDestroyTarget : MonoBehaviour
{
public static int Sum { get; set; }
[ShrinkSubscribe]
private void OnEvent(AwakeToDestroyEvent value) => Sum += value.Value;
}
internal sealed class ConcreteAwakeToDestroyTarget : AbstractAwakeToDestroyTarget
{
}
[ShrinkEventSubscriber(DefaultBus = "game")] [ShrinkEventSubscriber(DefaultBus = "game")]
internal sealed class MonoLifecycleTarget : MonoBehaviour internal sealed class MonoLifecycleTarget : MonoBehaviour
{ {
@@ -25,6 +60,53 @@ namespace ShrinkEventBus.PlayMode.Tests
public sealed class ShrinkMonoEventScopePlayModeTests public sealed class ShrinkMonoEventScopePlayModeTests
{ {
[UnityTest]
public IEnumerator AwakeToDestroyLifetimeIsInjectedWithoutManualBinding()
{
AwakeToDestroyTarget.Sum = 0;
AwakeToDestroyTarget.AwakeCalls = 0;
AwakeToDestroyTarget.DestroyCalls = 0;
var gameObject = new GameObject("ShrinkEventBus-AwakeToDestroy");
var target = gameObject.AddComponent<AwakeToDestroyTarget>();
yield return null;
Assert.IsInstanceOf<IShrinkGeneratedSubscriber>(target);
Assert.AreEqual(1, AwakeToDestroyTarget.AwakeCalls);
yield return EventBus.PostAsync(new AwakeToDestroyEvent(3)).ToCoroutine();
Assert.AreEqual(3, AwakeToDestroyTarget.Sum);
gameObject.SetActive(false);
yield return null;
yield return EventBus.PostAsync(new AwakeToDestroyEvent(5)).ToCoroutine();
Assert.AreEqual(8, AwakeToDestroyTarget.Sum,
"AwakeToDestroy subscriptions must survive OnDisable.");
Object.Destroy(gameObject);
yield return null;
Assert.AreEqual(1, AwakeToDestroyTarget.DestroyCalls);
yield return EventBus.PostAsync(new AwakeToDestroyEvent(7)).ToCoroutine();
Assert.AreEqual(8, AwakeToDestroyTarget.Sum,
"The generated OnDestroy release must remove the subscription.");
}
[UnityTest]
public IEnumerator AwakeToDestroyLifetimeSupportsAbstractSubscriberBasesWithoutLifecycleMethods()
{
AbstractAwakeToDestroyTarget.Sum = 0;
var gameObject = new GameObject("ShrinkEventBus-AbstractAwakeToDestroy");
var target = gameObject.AddComponent<ConcreteAwakeToDestroyTarget>();
yield return null;
Assert.IsInstanceOf<IShrinkGeneratedSubscriber>(target);
yield return EventBus.PostAsync(new AwakeToDestroyEvent(11)).ToCoroutine();
Assert.AreEqual(11, AbstractAwakeToDestroyTarget.Sum);
Object.Destroy(gameObject);
yield return null;
yield return EventBus.PostAsync(new AwakeToDestroyEvent(13)).ToCoroutine();
Assert.AreEqual(11, AbstractAwakeToDestroyTarget.Sum);
}
[UnityTest] [UnityTest]
public IEnumerator OnEnableAttachesAndOnDisableReleases() public IEnumerator OnEnableAttachesAndOnDisableReleases()
{ {
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>9.0</LangVersion>
<Nullable>enable</Nullable>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<AssemblyName>ShrinkEventBus.Core</AssemblyName>
<RootNamespace>ShrinkEventBus</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="ShrinkEventBus.cs" />
<Compile Include="Schedulers.cs" />
<Compile Include="../../../Runtime/ShrinkEventContracts.cs" Link="Runtime/ShrinkEventContracts.cs" />
<Compile Include="../../../Runtime/ShrinkEventAttributes.cs" Link="Runtime/ShrinkEventAttributes.cs" />
</ItemGroup>
</Project>
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>9.0</LangVersion>
<Nullable>enable</Nullable>
<IsRoslynComponent>true</IsRoslynComponent>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<IncludeBuildOutput>false</IncludeBuildOutput>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" PrivateAssets="all" />
</ItemGroup>
</Project>
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>9.0</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../ShrinkEventBus.Core/ShrinkEventBus.Core.csproj" />
<ProjectReference Include="../ShrinkEventBus.Generator/ShrinkEventBus.Generator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "com.cneicy.shrink-eventbus", "name": "com.cneicy.shrink-eventbus",
"version": "2.0.0", "version": "2.0.1",
"displayName": "ShrinkEventBus", "displayName": "ShrinkEventBus",
"description": "多 Bus、特性强类型注册、UniTask 调度与低分配发布的 Unity 事件总线。", "description": "多 Bus、特性强类型注册、UniTask 调度与低分配发布的 Unity 事件总线。",
"unity": "2022.3", "unity": "2022.3",