Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 0.9.3 #18

Merged
merged 1 commit into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [0.9.3] - 2024-11-13

**New**
- Added new *ITaskWaitState.WaitingFor(Func<UniTask>)* method to allow *TaskWaitStates* to hold on *UniTask* type calls

**Fixed**
- Added *UniTask* to the package to allow the *Statechart* run on WebGL

## [0.9.2] - 2024-10-25

**Fixed**
Expand All @@ -16,6 +24,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [0.9.0] - 2023-09-02

**New**
- Introduced unique identifiers for *IWaitActivity* instances to differentiate between different activities
- Enhanced the *TaskWaitState* class with a new event queuing mechanism to handle events after task completion
- Improved the *SplitState* class to allow pausing/resuming of inner state transitions
Expand All @@ -31,6 +40,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [0.7.0] - 2021-05-04

**New**
- Improved State documentations
- Improved State debug logs

Expand All @@ -53,6 +63,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [0.5.2] - 2020-09-27

**New**
- Added *IStatechartEvent* data to the logs
- Added logs to all possible trigger cases
- Added logs to the *ITaskWaitState* and *IWaitState* waiting call method
Expand All @@ -66,6 +77,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [0.5.0] - 2020-09-24

**New**
- Added new *ITransitionState* that acts as a non-blocker state between 2 different states

**Changed**
Expand All @@ -76,6 +88,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [0.4.0] - 2020-09-22

**New**
- Added the possibility to not execute *IStateExit.OnExit* on the current active state when leaving *IStateNest* or *IStateSplit*.
- Added the new *ITaskWaitState* to have a waiter for Task async methods. This state cannot have event triggers, for that use *IWaitState*

Expand All @@ -84,10 +97,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [0.3.0] - 2020-09-06

**New**
- Added the possibility to trigger events without a target state. Only *InitialState*, *ChoiceState* & *LeaveState* don't allow it due to the nature of their behaviour.

## [0.2.0] - 2020-08-27

**New**
- Added the possibility to always execute the *FinalState* of a *NestState* and * *SplitState*

**Changed**
Expand All @@ -98,13 +113,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [0.1.3] - 2020-01-06

**Fixed**
- Removed package dependency

## [0.1.2] - 2020-01-06

- Removed Preview label out of the package version
**New**
- Added NSubstitute dependency for the Unit Tests

**Fixed**
- Removed Preview label out of the package version

## [0.1.0] - 2020-01-05

- Initial submission for package distribution
5 changes: 4 additions & 1 deletion Runtime/GameLovers.Statechart.asmdef
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"name": "GameLovers.Statechart",
"references": [],
"rootNamespace": "",
"references": [
"GUID:f51ebe6a0ceec4240a699833d6309b23"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
Expand Down
4 changes: 4 additions & 0 deletions Runtime/IState.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Cysharp.Threading.Tasks;
using System;
using System.Threading.Tasks;

Expand Down Expand Up @@ -161,6 +162,9 @@ public interface ITaskWaitState : IStateEnter, IStateExit
/// It will return the created <see cref="ITransition"/> that will triggered as soon as the state is unblocked
/// </summary>
ITransition WaitingFor(Func<Task> taskAwaitAction);

/// <inheritdoc cref="WaitingFor(Func<Task>)"/>
ITransition WaitingFor(Func<UniTask> taskAwaitAction);
}

/// <summary>
Expand Down
21 changes: 18 additions & 3 deletions Runtime/Internal/TaskWaitState.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Cysharp.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
Expand All @@ -11,7 +12,7 @@ namespace GameLovers.StatechartMachine.Internal
internal class TaskWaitState : StateInternal, ITaskWaitState
{
private ITransitionInternal _transition;
private Func<Task> _taskAwaitAction;
private Func<UniTask> _taskAwaitAction;
private IStatechartEvent _eventQueued;
private bool _triggered;

Expand Down Expand Up @@ -106,13 +107,27 @@ public void OnExit(Action action)

/// <inheritdoc />
public ITransition WaitingFor(Func<Task> taskAwaitAction)
{
if (taskAwaitAction == null)
{
throw new NullReferenceException($"The state {Name} cannot have a null wait action");
}

_taskAwaitAction = () => taskAwaitAction().AsUniTask();
_transition = new Transition();

return _transition;
}

/// <inheritdoc />
public ITransition WaitingFor(Func<UniTask> taskAwaitAction)
{
_taskAwaitAction = taskAwaitAction ?? throw new NullReferenceException($"The state {Name} cannot have a null wait action");
_transition = new Transition();

return _transition;
}

/// <inheritdoc />
protected override ITransitionInternal OnTrigger(IStatechartEvent statechartEvent)
{
Expand All @@ -125,7 +140,7 @@ protected override ITransitionInternal OnTrigger(IStatechartEvent statechartEven
return Completed ? _transition : null;
}

private async Task InnerTaskAwait(string eventName)
private async UniTask InnerTaskAwait(string eventName)
{
try
{
Expand Down
2 changes: 2 additions & 0 deletions Tests/Editor/GameLovers.Statechart.Editor.Tests.asmdef
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
{
"name": "GameLovers.Statechart.Editor.Tests",
"rootNamespace": "",
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"UniTask",
"GameLovers.Statechart"
],
"includePlatforms": [
Expand Down
62 changes: 59 additions & 3 deletions Tests/Editor/StatechartTaskWaitTest.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using System;
using System.Collections;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using GameLovers.StatechartMachine;
using NSubstitute;
using NUnit.Framework;
Comment on lines +3 to 6

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

The using statements have been modified.

- using System.Collections;
+ using Cysharp.Threading.Tasks;

using UnityEngine.TestTools;

// ReSharper disable CheckNamespace

Expand Down Expand Up @@ -79,6 +78,33 @@ public async Task TaskWait_EventTrigger_DoesNothing()
_caller.Received().FinalOnEnterCall(0);
}

[Test]
public async Task UniTaskWait_EventTrigger_DoesNothing()
{
var statechart = new Statechart(SetupUniTaskWaitState);

statechart.Run();

_caller.Received().OnTransitionCall(0);
_caller.DidNotReceive().OnTransitionCall(1);
_caller.DidNotReceive().OnTransitionCall(2);
_caller.Received().InitialOnExitCall(0);
_caller.Received().StateOnEnterCall(0);
_caller.DidNotReceive().StateOnExitCall(0);
_caller.DidNotReceive().FinalOnEnterCall(0);

statechart.Trigger(_event);

_blocker = false;

await YieldWaitUniTask();

_caller.Received().OnTransitionCall(1);
_caller.DidNotReceive().OnTransitionCall(2);
_caller.Received().StateOnExitCall(0);
_caller.Received().FinalOnEnterCall(0);
}

Comment on lines +81 to +107

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

A new test method UniTaskWait_EventTrigger_DoesNothing() has been added to test UniTask waiting functionality.

[Test]
public async Task UniTaskWait_EventTrigger_DoesNothing()
{
    var statechart = new Statechart(SetupUniTaskWaitState);

    statechart.Run();

    _caller.Received().OnTransitionCall(0);
    _caller.DidNotReceive().OnTransitionCall(1);
    _caller.DidNotReceive().OnTransitionCall(2);
    _caller.Received().InitialOnExitCall(0);
    _caller.Received().StateOnEnterCall(0);
    _caller.DidNotReceive().StateOnExitCall(0);
    _caller.DidNotReceive().FinalOnEnterCall(0);

    statechart.Trigger(_event);

    _blocker = false;

    await YieldWaitUniTask();

    _caller.Received().OnTransitionCall(1);
    _caller.DidNotReceive().OnTransitionCall(2);
    _caller.Received().StateOnExitCall(0);
    _caller.Received().FinalOnEnterCall(0);
}

[Test]
public void TaskWait_MissingConfiguration_ThrowsException()
{
Expand Down Expand Up @@ -122,7 +148,17 @@ private async Task TaskWaitAction()

_done = true;
}


private async UniTask UniTaskWaitAction()
{
while (_blocker)
{
await UniTask.Yield();
}

_done = true;
}

Comment on lines +151 to +161

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

A new private method UniTaskWaitAction() has been added to handle UniTask waiting.

private async UniTask UniTaskWaitAction()
{
    while (_blocker)
    {
        await UniTask.Yield();
    }

    _done = true;
}

private async Task YieldWaitTask()
{
while (!_done)
Expand All @@ -133,6 +169,16 @@ private async Task YieldWaitTask()
await Task.Yield();
}

private async UniTask YieldWaitUniTask()
{
while (!_done)
{
await UniTask.Yield();
}

await UniTask.Yield();
}
Comment on lines +172 to +180

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

The YieldWaitTask() method has been replaced with YieldWaitUniTask() method to use UniTask instead of Task.

private async UniTask YieldWaitUniTask()
{
    while (!_done)
    {
        await UniTask.Yield();
    }

    await UniTask.Yield();
}


private IFinalState SetupSimpleFlow(IStateFactory factory, IState state)
{
var initial = factory.Initial("Initial");
Expand All @@ -155,5 +201,15 @@ private void SetupTaskWaitState(IStateFactory factory)
waiting.WaitingFor(TaskWaitAction).OnTransition(() => _caller.OnTransitionCall(1)).Target(final);
waiting.OnExit(() => _caller.StateOnExitCall(0));
}

private void SetupUniTaskWaitState(IStateFactory factory)
{
var waiting = factory.TaskWait("Task Wait");
var final = SetupSimpleFlow(factory, waiting);

waiting.OnEnter(() => _caller.StateOnEnterCall(0));
waiting.WaitingFor(UniTaskWaitAction).OnTransition(() => _caller.OnTransitionCall(1)).Target(final);
waiting.OnExit(() => _caller.StateOnExitCall(0));
}
Comment on lines +205 to +213

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

The SetupUniTaskWaitState() method has been added to set up the statechart for UniTask waiting.

private void SetupUniTaskWaitState(IStateFactory factory)
{
    var waiting = factory.TaskWait("Task Wait");
    var final = SetupSimpleFlow(factory, waiting);

    waiting.OnEnter(() => _caller.StateOnEnterCall(0));
    waiting.WaitingFor(UniTaskWaitAction).OnTransition(() => _caller.OnTransitionCall(1)).Target(final);
    waiting.OnExit(() => _caller.StateOnExitCall(0));
}

Overall, the changes seem fine. However, there are a few suggestions to improve the code:

  1. Consistency: Ensure consistent ordering of using statements. It's recommended to group them by namespace and separate them with a blank line.
  2. Naming: Consider using more descriptive names for variables and methods to improve code readability.
  3. Error Handling: Add appropriate error handling mechanisms, such as try-catch blocks, to handle exceptions that may occur during asynchronous operations.

Let me know if you have any questions or need further assistance!

}
}
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
"name": "com.gamelovers.statechart",
"displayName": "Statechart",
"author": "Miguel Tomas",
"version": "0.9.2",
"unity": "2022.4",
"version": "0.9.3",
"unity": "2022.3",
"license": "MIT",
"description": "This package allows the use of Statecharts (Hierarchichal State Machine) within an Unity project.\n\nThe primary feature of Statecharts is that states can be organized in a hierarchy.\nA Statecharts is a state machine where each state in the state machine may define its own subordinate state machines, called substates.\nThose states can again define substates.\n\nFor more information: https://statecharts.github.io/what-is-a-statechart.html",
"type": "library",
"hideInEditor": false
"hideInEditor": false,
"dependencies": {
"com.cysharp.unitask": "2.5.10"
}
}
Loading