From 528c37531663dd88c5b678bc4b0d5cacac20febe Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 27 May 2026 16:36:16 +0200 Subject: [PATCH 01/29] fix(UE): memory leaks --- .../Source/inkcpp/Private/InkChoice.cpp | 11 +- .../inkcpp/Source/inkcpp/Private/InkList.cpp | 17 +- .../Source/inkcpp/Private/InkRuntime.cpp | 35 ++- .../Source/inkcpp/Private/InkThread.cpp | 31 ++- .../inkcpp/Source/inkcpp/Private/InkVar.cpp | 144 +++++------ .../inkcpp/Source/inkcpp/Public/InkChoice.h | 20 +- .../inkcpp/Source/inkcpp/Public/InkHandles.h | 70 ++++++ unreal/inkcpp/Source/inkcpp/Public/InkList.h | 57 ++++- .../inkcpp/Source/inkcpp/Public/InkRuntime.h | 91 ++++--- .../inkcpp/Source/inkcpp/Public/InkThread.h | 46 +++- unreal/inkcpp/Source/inkcpp/Public/InkVar.h | 228 ++++++++++++------ unreal/inkcpp/Source/inkcpp/Public/inkcpp.h | 3 +- unreal/inkcpp/Source/inkcpp/inkcpp.Build.cs | 22 +- 13 files changed, 546 insertions(+), 229 deletions(-) create mode 100644 unreal/inkcpp/Source/inkcpp/Public/InkHandles.h diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkChoice.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkChoice.cpp index 365d2b4f..5c48b6b7 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkChoice.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkChoice.cpp @@ -8,17 +8,20 @@ #include "ink/choice.h" -FString UInkChoice::GetText() const { return data->text(); } - UInkChoice::UInkChoice() { tags = NewObject(); } -int UInkChoice::GetIndex() const { return data->index(); } +FString UInkChoice::GetText() const { return Text; } + +int UInkChoice::GetIndex() const { return Index; } const UTagList* UInkChoice::GetTags() const { return tags; } void UInkChoice::Initialize(const ink::runtime::choice* c) { - data = c; + // Copy all data out of the runner immediately — the pointer is only valid + // until the next getline() or choose() call. + Text = FString(UTF8_TO_TCHAR(c->text())); + Index = c->index(); if (c->has_tags()) { TArray fstring_tags{}; for (unsigned i = 0; i < c->num_tags(); ++i) { diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkList.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkList.cpp index 17ec5680..24d6090e 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkList.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkList.cpp @@ -4,19 +4,22 @@ * See file LICENSE.txt or go to * https://github.com/JBenda/inkcpp for full license details. */ -#pragma once - #include "InkList.h" +#include "inkcpp.h" #include #include "ink/list.h" bool UInkList::ContainsFlag(const FString& flag_name) const { + if (! ensureMsgf(IsValid(), TEXT("UInkList::ContainsFlag called on an invalid (stale) list"))) + return false; return list_data->contains(TCHAR_TO_UTF8(*flag_name)); } bool UInkList::ContainsEnum(const UEnum* Enum, const uint8& value) const { + if (! ensureMsgf(IsValid(), TEXT("UInkList::ContainsEnum called on an invalid (stale) list"))) + return false; if (! Enum) { UE_LOG( InkCpp, Warning, @@ -31,6 +34,8 @@ bool UInkList::ContainsEnum(const UEnum* Enum, const uint8& value) const TArray UInkList::ElementsOf(const UEnum* Enum) const { TArray ret; + if (! ensureMsgf(IsValid(), TEXT("UInkList::ElementsOf called on an invalid (stale) list"))) + return ret; if (! Enum) { UE_LOG(InkCpp, Warning, TEXT("Failed to provide enum for elements of!")); return ret; @@ -63,6 +68,10 @@ TArray UInkList::ElementsOf(const UEnum* Enum) const TArray UInkList::ElementsOfAsString(const UEnum* Enum) const { TArray ret; + if (! ensureMsgf( + IsValid(), TEXT("UInkList::ElementsOfAsString called on an invalid (stale) list") + )) + return ret; FString EnumName = Enum->GetFName().ToString(); for (auto itr = list_data->begin(TCHAR_TO_UTF8(*EnumName)); itr != list_data->end(); ++itr) { @@ -74,6 +83,8 @@ TArray UInkList::ElementsOfAsString(const UEnum* Enum) const TArray UInkList::Elements() const { TArray ret; + if (! ensureMsgf(IsValid(), TEXT("UInkList::Elements called on an invalid (stale) list"))) + return ret; for (auto itr = list_data->begin(); itr != list_data->end(); ++itr) { ret.Add(FListFlag{ .list_name = FString((*itr).list_name), @@ -85,5 +96,7 @@ TArray UInkList::Elements() const bool UInkList::ContainsList(const FString& name) const { + if (! ensureMsgf(IsValid(), TEXT("UInkList::ContainsList called on an invalid (stale) list"))) + return false; return list_data->begin(TCHAR_TO_UTF8(*name)) != list_data->end(); } diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index b549a643..511a664c 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -34,13 +34,7 @@ AInkRuntime::AInkRuntime() PrimaryActorTick.bCanEverTick = true; } -AInkRuntime::~AInkRuntime() -{ - if (mSnapshot) { - delete mpSnapshot; - } - mSnapshot.Reset(); -} +AInkRuntime::~AInkRuntime() {} // Called when the game starts or when spawned void AInkRuntime::BeginPlay() @@ -60,8 +54,6 @@ void AInkRuntime::BeginPlay() } else { mpGlobals = mpRuntime->new_globals(); } - // initialize globals - mpRuntime->new_runner(mpGlobals); } else { UE_LOG(InkCpp, Warning, TEXT("No story asset assigned.")); } @@ -69,6 +61,27 @@ void AInkRuntime::BeginPlay() Super::BeginPlay(); } +void AInkRuntime::EndPlay(const EEndPlayReason::Type EndPlayReason) +{ + Super::EndPlay(EndPlayReason); + + mThreads.Empty(); + mExclusiveStack.Empty(); + + mpGlobals = ink::runtime::globals{}; + + if (mpSnapshot) { + delete mpSnapshot; + mpSnapshot = nullptr; + } + mSnapshot.Reset(); + + if (mpRuntime) { + delete mpRuntime; + mpRuntime = nullptr; + } +} + // Called every frame void AInkRuntime::Tick(float DeltaTime) { @@ -161,6 +174,10 @@ FInkSnapshot AInkRuntime::Snapshot() void AInkRuntime::LoadSnapshot(const FInkSnapshot& snapshot) { + if (mpSnapshot) { + delete mpSnapshot; + mpSnapshot = nullptr; + } mSnapshot = snapshot; mpSnapshot = ink::runtime::snapshot::from_binary( reinterpret_cast(mSnapshot->data.GetData()), mSnapshot->data.Num(), false diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index 119ef2c9..483de9c8 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -15,6 +15,10 @@ // Unreal includes #include "Internationalization/Regex.h" +/** Thread-local pointer to the currently executing UInkThread, used by FInkVar to register + * newly created UInkList wrappers for later invalidation. */ +static thread_local UInkThread* GExecutingInkThread = nullptr; + UInkThread::UInkThread() : mbHasRun(false) , mnChoiceToChoose(-1) @@ -26,6 +30,23 @@ UInkThread::UInkThread() UInkThread::~UInkThread() {} +void UInkThread::RegisterLiveList(UInkList* list) +{ + if (list) { + mLiveLists.Add(list); + } +} + +void UInkThread::InvalidateLiveLists() +{ + for (auto& weak : mLiveLists) { + if (weak.IsValid()) { + weak->Invalidate(); + } + } + mLiveLists.Reset(); +} + void UInkThread::Yield() { mnYieldCounter++; } bool UInkThread::IsYielding() { return mnYieldCounter > 0; } @@ -127,6 +148,9 @@ bool UInkThread::ExecuteInternal() // Handle pending choice if (mnChoiceToChoose != -1) { if (ensure(mpRunner->num_choices() > 0)) { + // Invalidate all UInkList objects created from runner memory + // before the runner state changes. + InvalidateLiveLists(); mpRunner->choose(mnChoiceToChoose); } mnChoiceToChoose = -1; @@ -235,13 +259,16 @@ void UInkThread::ExecuteTagMethod(const TArray& Params) bool UInkThread::Execute() { + // Set the executing thread so FInkVar can register live lists + GExecutingInkThread = this; + // Execute thread bool finished = ExecuteInternal(); + GExecutingInkThread = nullptr; + // If we've finished, run callback if (finished) { - // Allow outsiders to subscribe - // TODO: OnThreadShutdown.Broadcast(); OnShutdown(); } diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp index 0a6d399b..76e98f3f 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp @@ -5,134 +5,134 @@ * https://github.com/JBenda/inkcpp for full license details. */ #include "InkVar.h" +#include "InkThread.h" #include "ink/types.h" #include "Misc/AssertionMacros.h" -FInkVar::FInkVar(ink::runtime::value val) : FInkVar() { +// Forward declaration of the executing thread pointer defined in InkThread.cpp +extern thread_local UInkThread* GExecutingInkThread; + +FInkVar::FInkVar(ink::runtime::value val) + : FInkVar() +{ using v_types = ink::runtime::value::Type; - switch(val.type) { - case v_types::Bool: value.SetSubtype(val.get()); break; + switch (val.type) { + case v_types::Bool: + BoolVal = val.get(); + VarType = EInkVarType::Bool; + break; case v_types::Uint32: - UE_LOG(InkCpp, Warning, TEXT("Converting uint to int, this will cause trouble if writing it back to ink (with SetGlobalVariable)!")); - value.SetSubtype(val.get()); + UE_LOG( + InkCpp, Warning, + TEXT("Converting uint to int, this will cause trouble if writing it back to ink (with " + "SetGlobalVariable)!") + ); + IntVal = ( int32 ) val.get(); + VarType = EInkVarType::Int; + break; + case v_types::Int32: + IntVal = val.get(); + VarType = EInkVarType::Int; + break; + case v_types::String: + StringVal = FString(UTF8_TO_TCHAR(val.get())); + VarType = EInkVarType::String; + break; + case v_types::Float: + FloatVal = val.get(); + VarType = EInkVarType::Float; break; - case v_types::Int32: value.SetSubtype(val.get()); break; - case v_types::String: value.SetSubtype(FString(val.get())); break; - case v_types::Float: value.SetSubtype(val.get()); break; case v_types::List: { - UInkList* list = NewObject(); - list->SetList(val.get()); - value.SetSubtype(list); + ListVal = NewObject(); + ListVal->SetList(val.get()); + VarType = EInkVarType::List; + // Register with the executing thread so it can invalidate when runner advances + if (GExecutingInkThread) { + GExecutingInkThread->RegisterLiveList(ListVal); + } break; } - default: - inkFail("unknown type!, failed to convert ink::value to InkVar"); + default: inkFail("unknown type!, failed to convert ink::value to InkVar"); } } - -ink::runtime::value FInkVar::to_value() const { - switch(type()) { - case EInkVarType::Int: - return ink::runtime::value(value.GetSubtype()); - case EInkVarType::Float: - return ink::runtime::value(value.GetSubtype()); + +ink::runtime::value FInkVar::to_value() const +{ + switch (VarType) { + case EInkVarType::Int: return ink::runtime::value(IntVal); + case EInkVarType::Float: return ink::runtime::value(FloatVal); case EInkVarType::String: - return ink::runtime::value(reinterpret_cast(utf8.GetData())); - case EInkVarType::Bool: - return ink::runtime::value(value.GetSubtype()); - case EInkVarType::UInt: - return ink::runtime::value(value.GetSubtype()); - case EInkVarType::List: - return ink::runtime::value(value.GetSubtype()->GetData()); - default: - inkFail("Unsupported type"); - return ink::runtime::value(); + return ink::runtime::value(reinterpret_cast(Utf8.GetData())); + case EInkVarType::Bool: return ink::runtime::value(BoolVal); + case EInkVarType::UInt: return ink::runtime::value(UIntVal); + case EInkVarType::List: return ink::runtime::value(ListVal->GetData()); + default: inkFail("Unsupported type"); return ink::runtime::value(); } - } -EInkVarType UInkVarLibrary::InkVarType(const FInkVar& InkVar) { return InkVar.type(); } +EInkVarType UInkVarLibrary::InkVarType(const FInkVar& InkVar) { return InkVar.VarType; } + FString UInkVarLibrary::Conv_InkVarString(const FInkVar& InkVar) { - if (ensureMsgf(InkVar.type() == EInkVarType::String, TEXT("InkVar is not a String Type!"))) - return InkVar.value.GetSubtype(); + if (ensureMsgf(InkVar.VarType == EInkVarType::String, TEXT("InkVar is not a String Type!"))) + return InkVar.StringVal; return FString(TEXT("")); } int UInkVarLibrary::Conv_InkVarInt(const FInkVar& InkVar) { - if (ensureMsgf(InkVar.type() == EInkVarType::Int, TEXT("InkVar is not an Int Type!"))) - return InkVar.value.GetSubtype(); + if (ensureMsgf(InkVar.VarType == EInkVarType::Int, TEXT("InkVar is not an Int Type!"))) + return InkVar.IntVal; return 0; } float UInkVarLibrary::Conv_InkVarFloat(const FInkVar& InkVar) { - if (ensureMsgf(InkVar.type() == EInkVarType::Float, TEXT("InkVar is not a Float Type!"))) - return InkVar.value.GetSubtype(); + if (ensureMsgf(InkVar.VarType == EInkVarType::Float, TEXT("InkVar is not a Float Type!"))) + return InkVar.FloatVal; return 0.f; } FName UInkVarLibrary::Conv_InkVarName(const FInkVar& InkVar) { - if (ensureMsgf(InkVar.type() == EInkVarType::String, TEXT("InkVar is not a String Type!"))) - return FName(*InkVar.value.GetSubtype()); + if (ensureMsgf(InkVar.VarType == EInkVarType::String, TEXT("InkVar is not a String Type!"))) + return FName(*InkVar.StringVal); return NAME_None; } FText UInkVarLibrary::Conv_InkVarText(const FInkVar& InkVar) { - if (ensureMsgf(InkVar.type() == EInkVarType::String, TEXT("InkVar is not a String Type!"))) - return FText::FromString(InkVar.value.GetSubtype()); + if (ensureMsgf(InkVar.VarType == EInkVarType::String, TEXT("InkVar is not a String Type!"))) + return FText::FromString(InkVar.StringVal); return FText::GetEmpty(); } bool UInkVarLibrary::Conv_InkVarBool(const FInkVar& InkVar) { - if (ensureMsgf(InkVar.type() == EInkVarType::Bool, TEXT("InkVar is not an Bool Type!"))) - return InkVar.value.GetSubtype(); + if (ensureMsgf(InkVar.VarType == EInkVarType::Bool, TEXT("InkVar is not an Bool Type!"))) + return InkVar.BoolVal; return false; } const UInkList* UInkVarLibrary::Conv_InkVarInkList(const FInkVar& InkVar) { - if (ensureMsgf(InkVar.type() == EInkVarType::List, TEXT("InkVar is not an List Type!"))) - return InkVar.value.GetSubtype(); + if (ensureMsgf(InkVar.VarType == EInkVarType::List, TEXT("InkVar is not an List Type!"))) + return InkVar.ListVal; return nullptr; } +FInkVar UInkVarLibrary::Conv_StringInkVar(const FString& String) { return FInkVar(String); } +FInkVar UInkVarLibrary::Conv_IntInkVar(int Number) { return FInkVar(Number); } -FInkVar UInkVarLibrary::Conv_StringInkVar(const FString& String) -{ - return FInkVar(String); -} +FInkVar UInkVarLibrary::Conv_FloatInkVar(float Number) { return FInkVar(Number); } -FInkVar UInkVarLibrary::Conv_IntInkVar(int Number) -{ - return FInkVar(Number); -} - -FInkVar UInkVarLibrary::Conv_FloatInkVar(float Number) -{ - return FInkVar(Number); -} +FInkVar UInkVarLibrary::Conv_TextInkVar(const FText& Text) { return FInkVar(Text.ToString()); } -FInkVar UInkVarLibrary::Conv_TextInkVar(const FText& Text) -{ - return FInkVar(Text.ToString()); -} +FInkVar UInkVarLibrary::Conv_NameInkVar(const FName& Name) { return FInkVar(Name.ToString()); } -FInkVar UInkVarLibrary::Conv_NameInkVar(const FName& Name) -{ - return FInkVar(Name.ToString()); -} - -FInkVar UInkVarLibrary::Conv_BoolInkVar(bool Boolean) -{ - return FInkVar(Boolean); -} +FInkVar UInkVarLibrary::Conv_BoolInkVar(bool Boolean) { return FInkVar(Boolean); } FInkVar UInkVarLibrary::Conv_ListInkVar(UInkList* List) { diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkChoice.h b/unreal/inkcpp/Source/inkcpp/Public/InkChoice.h index b256ddd6..e90367c0 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkChoice.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkChoice.h @@ -11,27 +11,28 @@ #include "InkChoice.generated.h" -namespace ink::runtime { class choice; } class UTagList; /** Representing a Ink Choice in the story flow * @ingroup unreal */ UCLASS(BlueprintType) -class UInkChoice : public UObject + +class INKCPP_API UInkChoice : public UObject { GENERATED_BODY() + public: UInkChoice(); - UFUNCTION(BlueprintPure, Category="Ink") + UFUNCTION(BlueprintPure, Category = "Ink") /** Access context of choice. * @return text contained in choice * @blueprint */ FString GetText() const; - UFUNCTION(BlueprintPure, Category="Ink") + UFUNCTION(BlueprintPure, Category = "Ink") /** Get identifier for @ref UInkThread::PickChoice() * @return id used in @ref UInkThread::PickChoice() * @@ -39,7 +40,7 @@ class UInkChoice : public UObject */ int GetIndex() const; - UFUNCTION(BlueprintPure, Category="Ink") + UFUNCTION(BlueprintPure, Category = "Ink") /** Tags associated with the choice. * @return with choice associated tags * @@ -49,10 +50,13 @@ class UInkChoice : public UObject protected: friend class UInkThread; - /** @private */ - void Initialize(const ink::runtime::choice*); + /** Copies choice data out of the runner. Safe to call after the runner advances. + * @private + */ + void Initialize(const ink::runtime::choice* c); private: - const ink::runtime::choice* data; + FString Text; + int Index = -1; TObjectPtr tags; }; diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkHandles.h b/unreal/inkcpp/Source/inkcpp/Public/InkHandles.h new file mode 100644 index 00000000..626a91e8 --- /dev/null +++ b/unreal/inkcpp/Source/inkcpp/Public/InkHandles.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2024 Julian Benda + * + * This file is part of inkCPP which is released under MIT license. + * See file LICENSE.txt or go to + * https://github.com/JBenda/inkcpp for full license details. + */ +#pragma once + +#include "CoreMinimal.h" +#include "Templates/SharedPointer.h" + +#include "InkHandles.generated.h" + +/** + * Handle returned by @ref AInkRuntime::ObserveVariable() and variants. + * Pass to @ref AInkRuntime::UnobserveVariable() to stop receiving callbacks. + * Letting the handle go out of scope does NOT automatically unregister — + * you must call UnobserveVariable() explicitly. + * @ingroup unreal + */ +USTRUCT(BlueprintType) + +struct INKCPP_API FInkObserverHandle { + GENERATED_BODY() + + /** @private */ + FInkObserverHandle() {} + + /** @private */ + explicit FInkObserverHandle(TSharedPtr token) + : Token(MoveTemp(token)) + { + } + + /** Returns true if this handle refers to an active observer registration. */ + bool IsValid() const { return Token.IsValid() && *Token; } + + /** @private */ + TSharedPtr Token; +}; + +/** + * Handle returned by @ref UInkThread::RegisterExternalFunction() and + * @ref UInkThread::RegisterExternalEvent(). + * Pass to @ref UInkThread::UnregisterExternalFunction() to remove the binding. + * @ingroup unreal + */ +USTRUCT(BlueprintType) + +struct INKCPP_API FExternalFunctionHandle { + GENERATED_BODY() + + /** @private */ + FExternalFunctionHandle() {} + + /** @private */ + explicit FExternalFunctionHandle(TSharedPtr token, FString name) + : Token(MoveTemp(token)) + , FunctionName(MoveTemp(name)) + { + } + + /** Returns true if this handle refers to an active external function registration. */ + bool IsValid() const { return Token.IsValid() && *Token; } + + /** @private */ + TSharedPtr Token; + /** @private */ + FString FunctionName; +}; diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkList.h b/unreal/inkcpp/Source/inkcpp/Public/InkList.h index 7bb6099a..ecd79b2e 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkList.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkList.h @@ -6,18 +6,22 @@ */ #pragma once +#include "CoreMinimal.h" + #include "InkList.generated.h" -namespace ink::runtime { - class list_interface; - using list = list_interface*; -} +namespace ink::runtime +{ +class list_interface; +using list = list_interface*; +} // namespace ink::runtime /** A single flag of a list. * @ingroup unreal */ USTRUCT(BlueprintType) -struct FListFlag { + +struct INKCPP_API FListFlag { GENERATED_BODY() UPROPERTY(BlueprintReadOnly, Category = "Ink") /** name of the list, the flag is part of */ @@ -29,9 +33,16 @@ struct FListFlag { /** * Allows reading ink lists. + * + * A UInkList wraps a pointer into runner-owned memory. It is only valid until + * the runner advances (i.e. until the next getline() or choose()). After that + * @ref IsValid() returns false and all accessor methods become no-ops that + * return empty/default values. Always check @ref IsValid() before using a list + * that was not obtained in the same Blueprint event. * @ingroup unreal */ UCLASS(Blueprintable, BlueprintType) + class INKCPP_API UInkList : public UObject { GENERATED_BODY() @@ -41,15 +52,37 @@ class INKCPP_API UInkList : public UObject UInkList() {} /** @private */ - UInkList(ink::runtime::list list) { list_data = list; } + UInkList(ink::runtime::list list) + : list_data(list) + { + } /** @private */ - void SetList(ink::runtime::list list) { list_data = list; } + void SetList(ink::runtime::list list) + { + list_data = list; + bValid = true; + } + + /** @private — called by UInkThread before advancing the runner */ + void Invalidate() { bValid = false; } + + UFUNCTION(BlueprintPure, Category = "Ink") + + /** + * Returns whether this list object still points to valid runner memory. + * A list becomes invalid once the runner advances past the line or variable + * read that produced it. Do not call any other methods on an invalid list. + * + * @blueprint + */ + bool IsValid() const { return bValid && list_data != nullptr; } UFUNCTION(BlueprintPure, Category = "Ink") /** Checks if a flag is contained (by name) * @attention If the flag name is not unique please use the full flag name aka * `list_name.flag_name` + * @retval false if the list is no longer valid * * @blueprint */ @@ -58,6 +91,7 @@ class INKCPP_API UInkList : public UObject UFUNCTION(BlueprintPure, Category = "Ink") /** checks if flag with the same spelling then the enum `value` is set in the list * @retval true if flag is contained in list + * @retval false if the list is no longer valid * * @blueprint */ @@ -65,13 +99,15 @@ class INKCPP_API UInkList : public UObject UFUNCTION(BlueprintPure, Category = "Ink") /** returns all values of a list with the same name as the enum + * @retval empty array if the list is no longer valid * * @blueprint */ TArray ElementsOf(const UEnum* Enum) const; UFUNCTION(BlueprintPure, Category = "Ink") - /** returns all flag as string contained in the list from a list with the same name as the Enum` + /** returns all flag as string contained in the list from a list with the same name as the Enum + * @retval empty array if the list is no longer valid * * @blueprint */ @@ -79,6 +115,7 @@ class INKCPP_API UInkList : public UObject UFUNCTION(BlueprintPure, Category = "Ink") /** returns all `list_name` `flag_name` tuples + * @retval empty array if the list is no longer valid * * @blueprint */ @@ -87,6 +124,7 @@ class INKCPP_API UInkList : public UObject UFUNCTION(BlueprintPure, Category = "Ink") /** check if at least one value of the given list is included, OR the list is empty * and associated with the list + * @retval false if the list is no longer valid * * @blueprint */ @@ -98,5 +136,6 @@ class INKCPP_API UInkList : public UObject ink::runtime::list GetData() const { return list_data; } - ink::runtime::list list_data; + ink::runtime::list list_data = nullptr; + bool bValid = true; }; diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h index 27f2905b..f34cc39f 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h @@ -12,6 +12,7 @@ #include "InkDelegates.h" #include "InkSnapshot.h" +#include "InkHandles.h" #include "ink/types.h" #include "ink/globals.h" @@ -21,17 +22,22 @@ class UInkThread; struct FInkVar; -namespace ink::runtime { class story; } + +namespace ink::runtime +{ +class story; +} // namespace ink::runtime /** Instantiated story with global variable storage and access, used to instantiate new threads. * @ingroup unreal */ UCLASS() + class INKCPP_API AInkRuntime : public AActor { GENERATED_BODY() - -public: + +public: // Sets default values for this actor's properties AInkRuntime(); ~AInkRuntime(); @@ -53,7 +59,7 @@ class INKCPP_API AInkRuntime : public AActor */ UInkThread* StartExisting(UInkThread* thread, FString path = "", bool runImmediately = true); - UFUNCTION(BlueprintCallable, Category="Ink") + UFUNCTION(BlueprintCallable, Category = "Ink") /** creates a snapshot of the current runtime state. * can be loladed with @ref #LoadSnapshot() * @@ -71,7 +77,7 @@ class INKCPP_API AInkRuntime : public AActor void LoadSnapshot(const FInkSnapshot& snapshot); - UFUNCTION(BlueprintCallable, Category="Ink") + UFUNCTION(BlueprintCallable, Category = "Ink") /** Marks a thread as "exclusive". * As long as it is running, no other threads will update. * @see #PopExclusiveThread() @@ -80,27 +86,27 @@ class INKCPP_API AInkRuntime : public AActor */ void PushExclusiveThread(UInkThread* Thread); - UFUNCTION(BlueprintCallable, Category="Ink") + UFUNCTION(BlueprintCallable, Category = "Ink") /** Removes a thread from the exclusive stack. * @see #PushExclusiveThread() * * @blueprint */ void PopExclusiveThread(UInkThread* Thread); - - UFUNCTION(BlueprintCallable, Category="Ink") + + UFUNCTION(BlueprintCallable, Category = "Ink") /** register a "tag function" * This function is executed if context or a tag in a special format appears * @see @ref TagFunction * * @blueprint */ - void RegisterTagFunction(FName functionName, const FTagFunctionDelegate & function); + void RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function); /** @private for internal use */ void HandleTagFunction(UInkThread* Caller, const TArray& Params); - - UFUNCTION(BlueprintCallable, Category="Ink") + + UFUNCTION(BlueprintCallable, Category = "Ink") /** Access a variable from the ink runtime. * variables are shared between all threads in the same runtime. * @param name of variable in ink script @@ -108,8 +114,8 @@ class INKCPP_API AInkRuntime : public AActor * @blueprint */ FInkVar GetGlobalVariable(const FString& name); - - UFUNCTION(BlueprintCallable, Category="Ink") + + UFUNCTION(BlueprintCallable, Category = "Ink") /** Sets a global variable inside the ink runtime. * @param name of variable in ink script * @param value new value for the variable @@ -118,60 +124,83 @@ class INKCPP_API AInkRuntime : public AActor */ void SetGlobalVariable(const FString& name, const FInkVar& value); - UFUNCTION(BlueprintCallable, Category="Ink") - /** Gets a ping if variable changes + UFUNCTION(BlueprintCallable, Category = "Ink") + /** Gets a ping if variable changes. + * @return handle — pass to UnobserveVariable() to stop receiving callbacks * @see #ObserverVariableEvent() #ObserverVariableChange() * * @blueprint */ - void ObserverVariable(const FString& variableName, const FVariableCallbackDelegate& callback); + FInkObserverHandle + ObserverVariable(const FString& variableName, const FVariableCallbackDelegate& callback); - UFUNCTION(BlueprintCallable, Category="Ink") - /** On variable change provides new value + UFUNCTION(BlueprintCallable, Category = "Ink") + /** On variable change provides new value. + * @return handle — pass to UnobserveVariable() to stop receiving callbacks * @see #ObserverVariable() #ObserverVariableChange() * * @blueprint */ - void ObserverVariableEvent(const FString& variableName, const FVariableCallbackDelegateNewValue& callback); + FInkObserverHandle ObserverVariableEvent( + const FString& variableName, const FVariableCallbackDelegateNewValue& callback + ); - UFUNCTION(BlueprintCallable, Category="Ink") + UFUNCTION(BlueprintCallable, Category = "Ink") /** On variable change provides old and new value. - * @see #ObserverVariableEvent() #ObserverVariable() - * @attention if the variable set for the firs time, the old value has value type @ref + * @return handle — pass to UnobserveVariable() to stop receiving callbacks + * @attention if the variable set for the first time, the old value has value type @ref * EInkVarType::None + * @see #ObserverVariableEvent() #ObserverVariable() + * + * @blueprint + */ + FInkObserverHandle ObserverVariableChange( + const FString& variableName, const FVariableCallbackDelegateNewOldValue& callback + ); + + UFUNCTION(BlueprintCallable, Category = "Ink") + /** Stop receiving variable-change notifications for a previously registered observer. + * @param handle the handle returned by ObserverVariable / ObserverVariableEvent / + * ObserverVariableChange * * @blueprint */ - void ObserverVariableChange(const FString& variableName, const FVariableCallbackDelegateNewOldValue& callback); + void UnobserveVariable(const FInkObserverHandle& handle); protected: /** Called when the game starts or when spawned */ virtual void BeginPlay() override; -public: + /** Called when the actor is being removed from play */ + virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; + +public: // Called every frame /** @private */ virtual void Tick(float DeltaTime) override; // Story asset used in this level - UPROPERTY(EditAnywhere, Category="Ink") + UPROPERTY(EditAnywhere, Category = "Ink") /** @private */ class UInkAsset* InkAsset; private: - ink::runtime::story* mpRuntime; + ink::runtime::story* mpRuntime; ink::runtime::globals mpGlobals; UPROPERTY() TArray mThreads; - + TMap mGlobalTagFunctions; UPROPERTY() TArray mExclusiveStack; - - // UPROPERTY(EditDefaultsOnly, Category="Ink") - TOptional mSnapshot; + // snapshot generates data when re-constructing the globals to allow reconstructing the threads - ink::runtime::snapshot* mpSnapshot; + TOptional mSnapshot; + ink::runtime::snapshot* mpSnapshot = nullptr; + + /** Active observer tokens. When UnobserveVariable() is called the token is set to false, + * the lambda checks it before firing and skips. Tokens are cleaned up lazily. */ + TArray> mObserverTokens; }; diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h index b8abaf0b..0fd749ba 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h @@ -11,6 +11,7 @@ #include "InkVar.h" #include "InkDelegates.h" +#include "InkList.h" #include "ink/runner.h" @@ -26,6 +27,7 @@ class UInkChoice; * @ingroup unreal */ UCLASS(Blueprintable) + class INKCPP_API UInkThread : public UObject { GENERATED_BODY() @@ -193,6 +195,11 @@ class INKCPP_API UInkThread : public UObject */ const UTagList* GetGlobalTags(); + /** Get choices from the last OnChoice event. + * @return array of choices available at the current choice point, empty if not at a choice + */ + const TArray& GetCurrentChoices() const { return mCurrentChoices; } + protected: /** @private */ @@ -201,6 +208,11 @@ class INKCPP_API UInkThread : public UObject /** @private */ virtual void OnLineWritten_Implementation(const FString& line, UTagList* tags) {} + /** @private */ + virtual void OnKnotEntered_Implementation(const UTagList* global_tags, const UTagList* knot_tags) + { + } + /** @private */ virtual void OnTag_Implementation(const FString& line) {} @@ -221,31 +233,47 @@ class INKCPP_API UInkThread : public UObject void ExecuteTagMethod(const TArray& Params); + /** Register a UInkList that was created from runner-owned memory during this thread's + * execution. The thread will call Invalidate() on all registered lists before the next + * choose() call so that stale pointers are detected early. + * @private + */ + void RegisterLiveList(UInkList* list); + + /** Invalidate all UInkList objects registered since the last choose(). + * Called internally before mpRunner->choose(). + * @private + */ + void InvalidateLiveLists(); + private: ink::runtime::runner mpRunner; UPROPERTY() - UTagList* mpTags; + UTagList* mpTags; UPROPERTY() - UTagList* mkTags = nullptr; + UTagList* mkTags = nullptr; UPROPERTY() - UTagList* mgTags = nullptr; + UTagList* mgTags = nullptr; UPROPERTY() - TArray mCurrentChoices; /// @TODO: make accessible? + TArray mCurrentChoices; + + /** Lists wrapping runner-owned memory, registered during current execute cycle. */ + TArray> mLiveLists; TMap mTagFunctions; FString mStartPath; bool mbHasRun; - int mnChoiceToChoose; - int mnYieldCounter; - bool mbInChoice; - bool mbKill; - bool mbInitialized; + int mnChoiceToChoose; + int mnYieldCounter; + bool mbInChoice; + bool mbKill; + bool mbInitialized; ink::hash_t mCurrentKnot; UPROPERTY() diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h index 4449e9a8..52d3439a 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h @@ -3,16 +3,13 @@ * This file is part of inkCPP which is released under MIT license. * See file LICENSE.txt or go to * https://github.com/JBenda/inkcpp for full license details. - * - * Based on Copyright (c) 2020 David Colson - * UnrealInk @ https://github.com/DavidColson/UnrealInk */ #pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" +#include "inkcpp.h" #include "UObject/TextProperty.h" -#include "Containers/Union.h" #include "Containers/StringConv.h" #include "InkList.h" @@ -23,18 +20,20 @@ * @ingroup unreal */ UENUM(BlueprintType) -enum class EInkVarType : uint8 -{ +enum class EInkVarType : uint8 { Float, ///< contains a value of type float - Int, ///< contains a value of type int or uint - UInt, ///< @todo currently not supported + Int, ///< contains a value of type int + UInt, ///< @todo currently not supported in Blueprints (converts to signed) Bool, ///< contains a boolean String, ///< contains a string value List, ///< contains a @ref UInkList None ///< contains no value }; -namespace ink::runtime { struct value; } +namespace ink::runtime +{ +struct value; +} // namespace ink::runtime /** A wrapper for passing around ink vars to and from ink itself. * To access the values see @ref UInkVarLibrary @@ -42,36 +41,64 @@ namespace ink::runtime { struct value; } * @ingroup unreal */ USTRUCT(BlueprintType) -struct INKCPP_API FInkVar -{ + +struct INKCPP_API FInkVar { GENERATED_BODY() - FInkVar() {} + FInkVar() + : VarType(EInkVarType::None) + , IntVal(0) + { + } /** @private */ - FInkVar(float val) { value.SetSubtype(val); } + FInkVar(float val) + : VarType(EInkVarType::Float) + , FloatVal(val) + { + } /** @private */ - FInkVar(int val) { value.SetSubtype(val); } + FInkVar(int val) + : VarType(EInkVarType::Int) + , IntVal(val) + { + } /** @private */ FInkVar(unsigned val) + : VarType(EInkVarType::UInt) + , UIntVal(val) { - UE_LOG(InkCpp, Warning, TEXT("Converting unsigned to signed int, since missing blueprint support for unsigned type")); - value.SetSubtype(val); - } // TODO: change if we find a way to support unsigned values in blueprints + UE_LOG( + InkCpp, Warning, + TEXT("Converting unsigned to signed int, since missing blueprint support for unsigned type") + ); + } /** @private */ - FInkVar(bool val) { value.SetSubtype(val); } + FInkVar(bool val) + : VarType(EInkVarType::Bool) + , BoolVal(val) + { + } /** @private */ - FInkVar(FString val) { - value.SetSubtype(val); + FInkVar(FString val) + : VarType(EInkVarType::String) + , IntVal(0) + , StringVal(MoveTemp(val)) + { BufferDecodedString(); } /** @private */ - FInkVar(UInkList& List) { value.SetSubtype(&List); } + FInkVar(UInkList& List) + : VarType(EInkVarType::List) + , IntVal(0) + , ListVal(&List) + { + } /** @private */ FInkVar(ink::runtime::value val); @@ -79,40 +106,56 @@ struct INKCPP_API FInkVar /** @private */ ink::runtime::value to_value() const; + /** Get the type contained in the value + * @retval EInkVarType::None if no value is contained (void) + * @private + */ + EInkVarType type() const { return VarType; } + + // ----------------------------------------------------------------------- + // Data — laid out as explicit named fields so the GC can see ListVal and + // StringVal, and there is no union obscuring UObject* from the reflector. + // ----------------------------------------------------------------------- - // allow changing via Editor, but not in control flow, it is just a wrapper type to create a new - // one UPROPERTY(EditAnywhere, Category = "Ink") /** @private */ - TUnion value; + UPROPERTY() + EInkVarType VarType = EInkVarType::None; - /** Keeps utf8 version of string alive to write it in runtime. + // Scalar storage — only one is valid at a time based on VarType. + // Not UPROPERTY because they hold primitive types the GC does not need. + /** @private */ float FloatVal = 0.f; + /** @private */ int32 IntVal = 0; + /** @private */ uint32 UIntVal = 0; + /** @private */ bool BoolVal = false; + + /** String storage — separate field so it is always properly constructed/destroyed. * @private */ - TArray utf8{}; + FString StringVal; - /** Get the type contained in the value - * @retval EInkVarType::None if no value is contained (void) + /** List storage — UPROPERTY so the GC can trace the UObject*. + * Only valid when VarType == EInkVarType::List. * @private */ - EInkVarType type() const { - uint8 id = value.GetCurrentSubtypeIndex(); - if(id >= static_cast(EInkVarType::None)) - { return EInkVarType::None; } - else - { return static_cast(id); } - } + UPROPERTY() + TObjectPtr ListVal = nullptr; + + /** Keeps utf8 version of the string alive to write it back to the ink runtime. + * @private + */ + TArray Utf8{}; + private: - void BufferDecodedString() { - FTCHARToUTF8 Convert(*value.GetSubtype()); - utf8.SetNum(Convert.Length() + 1); - UTF8CHAR* dst = utf8.GetData(); - [this,&dst](const UTF8CHAR* src){ - int i = 0; - while(*src) { - *dst++ = *src++; - } - *dst = static_cast(0); - }(reinterpret_cast(Convert.Get())); + void BufferDecodedString() + { + FTCHARToUTF8 Convert(*StringVal); + Utf8.SetNum(Convert.Length() + 1); + UTF8CHAR* dst = Utf8.GetData(); + const UTF8CHAR* src = reinterpret_cast(Convert.Get()); + while (*src) { + *dst++ = *src++; + } + *dst = static_cast(0); } }; @@ -120,6 +163,7 @@ struct INKCPP_API FInkVar * @ingroup unreal */ UCLASS() + class INKCPP_API UInkVarLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() @@ -133,96 +177,141 @@ class INKCPP_API UInkVarLibrary : public UBlueprintFunctionLibrary */ static EInkVarType InkVarType(const FInkVar& InkVar); - UFUNCTION(BlueprintPure, meta = (DisplayName = "String (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + UFUNCTION( + BlueprintPure, + meta = (DisplayName = "String (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), + Category = "Ink" + ) /** Access String value * * @blueprint */ static FString Conv_InkVarString(const FInkVar& InkVar); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Int (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") - /** Access Int/Uint value - * @todo support unsigned int + UFUNCTION( + BlueprintPure, + meta = (DisplayName = "Int (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), + Category = "Ink" + ) + /** Access Int value * * @blueprint */ static int Conv_InkVarInt(const FInkVar& InkVar); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Float (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + UFUNCTION( + BlueprintPure, + meta = (DisplayName = "Float (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), + Category = "Ink" + ) /** Access Float Value * * @blueprint */ static float Conv_InkVarFloat(const FInkVar& InkVar); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Name (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + UFUNCTION( + BlueprintPure, + meta = (DisplayName = "Name (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), + Category = "Ink" + ) /** Access String value as FName * * @blueprint */ static FName Conv_InkVarName(const FInkVar& InkVar); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Text (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + UFUNCTION( + BlueprintPure, + meta = (DisplayName = "Text (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), + Category = "Ink" + ) /** Access String value as FText * * @blueprint */ static FText Conv_InkVarText(const FInkVar& InkVar); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Bool (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + UFUNCTION( + BlueprintPure, + meta = (DisplayName = "Bool (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), + Category = "Ink" + ) /** Access bool value - * * @blueprint */ static bool Conv_InkVarBool(const FInkVar& InkVar); - UFUNCTION(BlueprintPure, meta = (DisplayName = "InkList (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + UFUNCTION( + BlueprintPure, + meta = (DisplayName = "InkList (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), + Category = "Ink" + ) /** Access @ref UInkList "List" value * * @blueprint */ static const UInkList* Conv_InkVarInkList(const FInkVar& InkVar); - - // UFUNCTION(BlueprintPure, meta = (DisplayName = "UInt (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") - // static unsigned Conv_InkVarUInt(const FInkVar& InkVar); - - UFUNCTION(BlueprintPure, meta = (DisplayName = "Ink Var (String)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + UFUNCTION( + BlueprintPure, + meta = (DisplayName = "Ink Var (String)", CompactNodeTitle = "->", BlueprintAutocast), + Category = "Ink" + ) /** Convert string to @ref FInkVar * * @blueprint */ static FInkVar Conv_StringInkVar(const FString& String); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Ink Var (Int)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + UFUNCTION( + BlueprintPure, + meta = (DisplayName = "Ink Var (Int)", CompactNodeTitle = "->", BlueprintAutocast), + Category = "Ink" + ) /** Convert int to @ref FInkVar - * @todo support unsigned values * * @blueprint */ static FInkVar Conv_IntInkVar(int Number); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Ink Var (Float)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + UFUNCTION( + BlueprintPure, + meta = (DisplayName = "Ink Var (Float)", CompactNodeTitle = "->", BlueprintAutocast), + Category = "Ink" + ) /** Convert float to @ref FInkVar * * @blueprint */ static FInkVar Conv_FloatInkVar(float Number); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Ink Var (Text)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + UFUNCTION( + BlueprintPure, + meta = (DisplayName = "Ink Var (Text)", CompactNodeTitle = "->", BlueprintAutocast), + Category = "Ink" + ) /** Convert FText to @ref FInkVar of type @ref EInkVarType::String "String" * * @blueprint */ static FInkVar Conv_TextInkVar(const FText& Text); - - UFUNCTION(BlueprintPure, meta = (DisplayName = "Ink Var (Name)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + + UFUNCTION( + BlueprintPure, + meta = (DisplayName = "Ink Var (Name)", CompactNodeTitle = "->", BlueprintAutocast), + Category = "Ink" + ) /** Convert FName to @ref FInkVar of type @ref EInkVarType::String "String" * * @blueprint */ static FInkVar Conv_NameInkVar(const FName& Name); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Ink Var (Bool)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + UFUNCTION( + BlueprintPure, + meta = (DisplayName = "Ink Var (Bool)", CompactNodeTitle = "->", BlueprintAutocast), + Category = "Ink" + ) /** Convert bool to @ref FInkVar * * @blueprint @@ -239,7 +328,4 @@ class INKCPP_API UInkVarLibrary : public UBlueprintFunctionLibrary * @blueprint */ static FInkVar Conv_ListInkVar(UInkList* List); - - // UFUNCTION(BlueprintPure, meta = (DisplayName = "Ink Var (UInt)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") - // static FInkVar Conv_UIntInkVar(unsigned Number); }; diff --git a/unreal/inkcpp/Source/inkcpp/Public/inkcpp.h b/unreal/inkcpp/Source/inkcpp/Public/inkcpp.h index 76cf0751..ffb0f0c8 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/inkcpp.h +++ b/unreal/inkcpp/Source/inkcpp/Public/inkcpp.h @@ -20,7 +20,8 @@ * -TargetPlatforms=Win64` * ```
And either way activating the plugin. * - * The C++ API will be available soon([Issue](https://github.com/JBenda/inkcpp/issues/60)). + * The C++ API is available — include the plugin headers directly from your game module + * after adding `"inkcpp"` to your module's `PublicDependencyModuleNames` in Build.cs. * * + @ref ue_setup "General setup" * + @ref ue_components "UE5 Blueprints" diff --git a/unreal/inkcpp/Source/inkcpp/inkcpp.Build.cs b/unreal/inkcpp/Source/inkcpp/inkcpp.Build.cs index c9639147..0daecba8 100644 --- a/unreal/inkcpp/Source/inkcpp/inkcpp.Build.cs +++ b/unreal/inkcpp/Source/inkcpp/inkcpp.Build.cs @@ -14,21 +14,21 @@ public inkcpp(ReadOnlyTargetRules Target) : base(Target) PublicIncludePaths.AddRange( new string[] { - Path.Combine(ModuleDirectory, "../shared/Public") + Path.Combine(ModuleDirectory, "../shared/Public"), + Path.Combine(ModuleDirectory, "Public/ink"), } ); - - + + PrivateIncludePaths.AddRange( new string[] { Path.Combine(ModuleDirectory, "../shared/Private"), Path.Combine(ModuleDirectory, "Private/ink"), - Path.Combine(ModuleDirectory, "Public/ink"), Path.Combine(ModuleDirectory, "../ThirdParty/Private"), } ); - - + + PublicDependencyModuleNames.AddRange( new string[] { @@ -36,8 +36,8 @@ public inkcpp(ReadOnlyTargetRules Target) : base(Target) // ... add other public dependencies that you statically link with here ... } ); - - + + PrivateDependencyModuleNames.AddRange( new string[] { @@ -45,11 +45,11 @@ public inkcpp(ReadOnlyTargetRules Target) : base(Target) "Engine", "Slate", "SlateCore", - // ... add private dependencies that you statically link with here ... + // ... add private dependencies that you statically link with here ... } ); - - + + DynamicallyLoadedModuleNames.AddRange( new string[] { From 6c9ba6df15751ea1f76a5d67d41510cd3a422189 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 28 May 2026 22:13:31 +0200 Subject: [PATCH 02/29] WIP --- inkcpp/CMakeLists.txt | 2 +- inkcpp/header.cpp | 2 +- inkcpp/include/list.h | 1 + inkcpp/list_impl.h | 10 ++++++++-- inkcpp/story_impl.cpp | 6 ++---- inkcpp_python/pybind11 | 2 +- shared/private/header.h | 2 +- shared/public/system.h | 2 ++ unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp | 9 --------- unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp | 10 ++-------- unreal/inkcpp/Source/inkcpp/Public/InkChoice.h | 3 +++ unreal/inkcpp/Source/inkcpp/Public/InkVar.h | 4 +++- 12 files changed, 25 insertions(+), 28 deletions(-) diff --git a/inkcpp/CMakeLists.txt b/inkcpp/CMakeLists.txt index 942a517e..37353c02 100644 --- a/inkcpp/CMakeLists.txt +++ b/inkcpp/CMakeLists.txt @@ -83,7 +83,7 @@ endforeach() foreach(file IN LISTS PUBLIC_HEADERS) get_filename_component(file "${file}" NAME) configure_file("include/${file}" - "${CMAKE_BINARY_DIR}/unreal/inkcpp/Source/inkcpp/Public/ink/${FILE}" COPYONLY) + "${CMAKE_BINARY_DIR}/unreal/inkcpp/Source/inkcpp/Public/ink/${file}" COPYONLY) endforeach() foreach(file IN LISTS COLLECTION_SOURCES) configure_file("${file}" "${CMAKE_BINARY_DIR}/unreal/inkcpp/Source/inkcpp/Private/ink/${file}" diff --git a/inkcpp/header.cpp b/inkcpp/header.cpp index e92ee2a1..03f08226 100644 --- a/inkcpp/header.cpp +++ b/inkcpp/header.cpp @@ -10,7 +10,7 @@ namespace ink::internal { -bool header::verify() const +bool header::validate() const { if (endian() == endian_types::none) { inkFail("Header magic number was wrong!"); diff --git a/inkcpp/include/list.h b/inkcpp/include/list.h index 5f7cf42c..c1354dcc 100644 --- a/inkcpp/include/list.h +++ b/inkcpp/include/list.h @@ -81,6 +81,7 @@ class list_interface } public: + iterator(const iterator&) = default; /** contains flag data */ struct Flag { const char* flag_name; ///< name of the flag diff --git a/inkcpp/list_impl.h b/inkcpp/list_impl.h index 1aefb2be..82023c9a 100644 --- a/inkcpp/list_impl.h +++ b/inkcpp/list_impl.h @@ -21,6 +21,11 @@ class list_impl final : public list_interface { } + list_interface& operator=(const list_interface& oth) noexcept override + { + return *this = static_cast(oth); + } + list_impl& operator=(const list_impl&) = default; ~list_impl() override {} @@ -41,7 +46,8 @@ class list_impl final : public list_interface friend ink::runtime::internal::value; /// @todo wrong iteration order, first lists then flags - void next(const char*& flag_name, const char*& list_name, int& i, bool one_list_only) - const override; + void next( + const char*& flag_name, const char*& list_name, int& i, bool one_list_only + ) const override; }; } // namespace ink::runtime::internal diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index 32f8b3f2..8c3c9e2c 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -5,13 +5,11 @@ * https://github.com/JBenda/inkcpp for full license details. */ #include "story_impl.h" -#include "platform.h" #include "runner_impl.h" #include "globals_impl.h" #include "snapshot.h" #include "snapshot_impl.h" #include "snapshot_interface.h" -#include "version.h" namespace ink::runtime { @@ -149,7 +147,7 @@ container_t story_impl::find_container_for(uint32_t offset) const // know that the parent contained the child, but the containers are sparse so we might // not have anything. container_t id = entry ? entry->_id : ~0; - while (id != ~0) { + while (id != ~0U) { const container_data_t& data = container_data(id); if (data._start_offset <= offset && data._end_offset >= offset) return id; @@ -248,7 +246,7 @@ runner story_impl::new_runner_from_snapshot(const snapshot& data, globals store, void story_impl::setup_pointers() { const ink::internal::header& header = *reinterpret_cast(_file); - if (! header.verify()) { + if (! header.validate()) { return; } diff --git a/inkcpp_python/pybind11 b/inkcpp_python/pybind11 index 1b499083..0c69e1eb 160000 --- a/inkcpp_python/pybind11 +++ b/inkcpp_python/pybind11 @@ -1 +1 @@ -Subproject commit 1b4990838904501de7110d27e96c0a4152029156 +Subproject commit 0c69e1eb2177fa8f8580632c7b1f97fdb606ce8f diff --git a/shared/private/header.h b/shared/private/header.h index 43383f15..198b989c 100644 --- a/shared/private/header.h +++ b/shared/private/header.h @@ -37,7 +37,7 @@ struct header { } } - bool verify() const; + bool validate() const; struct section_t { uint32_t _start = 0; diff --git a/shared/public/system.h b/shared/public/system.h index 82259cbd..196897ea 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -258,6 +258,8 @@ void ink_assert(bool condition, const char* msg = nullptr, Args... args) #elif defined(INK_ENABLE_CSTD) fprintf(stderr, "Ink Assert: %s\n", msg); abort(); +#elif defined(INK_ENABLE_UNREAL) + // TODO: implement UE exception handling #else # warning no assertion handling this could lead to invalid code paths #endif diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index 483de9c8..07469e42 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -15,10 +15,6 @@ // Unreal includes #include "Internationalization/Regex.h" -/** Thread-local pointer to the currently executing UInkThread, used by FInkVar to register - * newly created UInkList wrappers for later invalidation. */ -static thread_local UInkThread* GExecutingInkThread = nullptr; - UInkThread::UInkThread() : mbHasRun(false) , mnChoiceToChoose(-1) @@ -259,14 +255,9 @@ void UInkThread::ExecuteTagMethod(const TArray& Params) bool UInkThread::Execute() { - // Set the executing thread so FInkVar can register live lists - GExecutingInkThread = this; - // Execute thread bool finished = ExecuteInternal(); - GExecutingInkThread = nullptr; - // If we've finished, run callback if (finished) { OnShutdown(); diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp index 76e98f3f..0aba463d 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp @@ -10,10 +10,7 @@ #include "Misc/AssertionMacros.h" -// Forward declaration of the executing thread pointer defined in InkThread.cpp -extern thread_local UInkThread* GExecutingInkThread; - -FInkVar::FInkVar(ink::runtime::value val) +FInkVar::FInkVar(ink::runtime::value val, UInkThread* thread) : FInkVar() { using v_types = ink::runtime::value::Type; @@ -47,10 +44,7 @@ FInkVar::FInkVar(ink::runtime::value val) ListVal = NewObject(); ListVal->SetList(val.get()); VarType = EInkVarType::List; - // Register with the executing thread so it can invalidate when runner advances - if (GExecutingInkThread) { - GExecutingInkThread->RegisterLiveList(ListVal); - } + thread->RegisterLiveList(ListVal); break; } default: inkFail("unknown type!, failed to convert ink::value to InkVar"); diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkChoice.h b/unreal/inkcpp/Source/inkcpp/Public/InkChoice.h index e90367c0..16d140da 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkChoice.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkChoice.h @@ -9,8 +9,11 @@ #include "UObject/Object.h" +#include "ink/choice.h" + #include "InkChoice.generated.h" + class UTagList; /** Representing a Ink Choice in the story flow diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h index 52d3439a..e5bb767e 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h @@ -7,6 +7,7 @@ #pragma once #include "CoreMinimal.h" +#include "InkRuntime.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "inkcpp.h" #include "UObject/TextProperty.h" @@ -93,11 +94,12 @@ struct INKCPP_API FInkVar { } /** @private */ - FInkVar(UInkList& List) + FInkVar(UInkList& List, UInkThread* thread) : VarType(EInkVarType::List) , IntVal(0) , ListVal(&List) { + thread->RegisterLiveList(ListVal); } /** @private */ From ffd0d5389c5435c978b54cb6143e790c0bbf09c9 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 29 May 2026 11:13:11 +0200 Subject: [PATCH 03/29] WIP2 --- inkcpp_python/pybind11 | 2 +- .../Source/inkcpp/Private/InkThread.cpp | 11 ++++++++--- .../inkcpp/Source/inkcpp/Private/InkVar.cpp | 19 ++++++++++++++++--- unreal/inkcpp/Source/inkcpp/Public/InkVar.h | 4 +--- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/inkcpp_python/pybind11 b/inkcpp_python/pybind11 index 0c69e1eb..1b499083 160000 --- a/inkcpp_python/pybind11 +++ b/inkcpp_python/pybind11 @@ -1 +1 @@ -Subproject commit 0c69e1eb2177fa8f8580632c7b1f97fdb606ce8f +Subproject commit 1b4990838904501de7110d27e96c0a4152029156 diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index 07469e42..47823424 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -11,10 +11,13 @@ #include "TagList.h" #include "InkChoice.h" #include "ink/runner.h" +#include "InkExecutionScope.h" // Unreal includes #include "Internationalization/Regex.h" +thread_local UInkThread* GExecutingInkThread = nullptr; + UInkThread::UInkThread() : mbHasRun(false) , mnChoiceToChoose(-1) @@ -255,15 +258,17 @@ void UInkThread::ExecuteTagMethod(const TArray& Params) bool UInkThread::Execute() { - // Execute thread + // RAII guard: sets GExecutingInkThread for this scope so that FInkVar + // constructors can register newly created UInkList wrappers with this + // thread. Automatically cleared on return (including via exception). + FInkExecutionScope scope(this); + bool finished = ExecuteInternal(); - // If we've finished, run callback if (finished) { OnShutdown(); } - // Return result return finished; } diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp index 0aba463d..12e78883 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp @@ -5,12 +5,18 @@ * https://github.com/JBenda/inkcpp for full license details. */ #include "InkVar.h" -#include "InkThread.h" +#include "InkExecutionScope.h" #include "ink/types.h" #include "Misc/AssertionMacros.h" -FInkVar::FInkVar(ink::runtime::value val, UInkThread* thread) +#include "InkVar.h" +#include "InkExecutionScope.h" +#include "ink/types.h" + +#include "Misc/AssertionMacros.h" + +FInkVar::FInkVar(ink::runtime::value val) : FInkVar() { using v_types = ink::runtime::value::Type; @@ -44,7 +50,14 @@ FInkVar::FInkVar(ink::runtime::value val, UInkThread* thread) ListVal = NewObject(); ListVal->SetList(val.get()); VarType = EInkVarType::List; - thread->RegisterLiveList(ListVal); + // Register with the executing thread (if any) via the RAII-guarded + // thread-local so the thread can invalidate this list before choose(). + // GExecutingInkThread is null when called outside Execute() (e.g. + // GetGlobalVariable), in which case the list is globals-lifetime and + // needs no per-choice invalidation. + if (GExecutingInkThread) { + GExecutingInkThread->RegisterLiveList(ListVal); + } break; } default: inkFail("unknown type!, failed to convert ink::value to InkVar"); diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h index e5bb767e..52d3439a 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h @@ -7,7 +7,6 @@ #pragma once #include "CoreMinimal.h" -#include "InkRuntime.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "inkcpp.h" #include "UObject/TextProperty.h" @@ -94,12 +93,11 @@ struct INKCPP_API FInkVar { } /** @private */ - FInkVar(UInkList& List, UInkThread* thread) + FInkVar(UInkList& List) : VarType(EInkVarType::List) , IntVal(0) , ListVal(&List) { - thread->RegisterLiveList(ListVal); } /** @private */ From bae509739c7959b3aaa46d09b9dd502b485315d3 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 29 May 2026 15:35:14 +0200 Subject: [PATCH 04/29] WIP3 --- .../Source/inkcpp/Private/InkExecutionScope.h | 28 ++++++ .../Source/inkcpp/Private/InkRuntime.cpp | 71 ++++++++++---- .../Source/inkcpp/Private/InkThread.cpp | 94 ++++++++++++++++--- .../inkcpp/Source/inkcpp/Public/InkHandles.h | 55 ++++------- .../inkcpp/Source/inkcpp/Public/InkRuntime.h | 44 +++++---- .../inkcpp/Source/inkcpp/Public/InkThread.h | 52 +++++++--- 6 files changed, 247 insertions(+), 97 deletions(-) create mode 100644 unreal/inkcpp/Source/inkcpp/Private/InkExecutionScope.h diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkExecutionScope.h b/unreal/inkcpp/Source/inkcpp/Private/InkExecutionScope.h new file mode 100644 index 00000000..5cd14d68 --- /dev/null +++ b/unreal/inkcpp/Source/inkcpp/Private/InkExecutionScope.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2024 Julian Benda + * + * This file is part of inkCPP which is released under MIT license. + * See file LICENSE.txt or go to + * https://github.com/JBenda/inkcpp for full license details. + */ +#pragma once + +class UInkThread; + +/** Thread-local pointer to the UInkThread currently executing inside Execute(). + * Set exclusively via FInkExecutionScope — never write it directly. + * Used by FInkVar to register newly created UInkList wrappers so the thread + * can invalidate them before the next choose() call. + */ +extern thread_local UInkThread* GExecutingInkThread; + +/** RAII guard that sets/clears GExecutingInkThread for the lifetime of a + * UInkThread::Execute() call. Impossible to forget to clear. + */ +struct FInkExecutionScope { + explicit FInkExecutionScope(UInkThread* thread) { GExecutingInkThread = thread; } + + ~FInkExecutionScope() { GExecutingInkThread = nullptr; } + + FInkExecutionScope(const FInkExecutionScope&) = delete; + FInkExecutionScope& operator=(const FInkExecutionScope&) = delete; +}; diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index 511a664c..2e8646fd 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -131,19 +131,33 @@ void AInkRuntime::Tick(float DeltaTime) } } -void AInkRuntime::HandleTagFunction(UInkThread* Caller, const TArray& Params) +FInkHandle + AInkRuntime::RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function) { - // Look for method and execute with parameters - FGlobalTagFunctionMulticastDelegate* function = mGlobalTagFunctions.Find(FName(*Params[0])); - if (function != nullptr) { - function->Broadcast(Caller, Params); - } + auto token = MakeShared(true); + mTagFunctionTokens.FindOrAdd(functionName).Add(token); + mTagFunctionDelegates.FindOrAdd(functionName).Add(function); + mObserverTokens.Add(token); + return FInkHandle(token); } -void AInkRuntime::RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function) +void AInkRuntime::HandleTagFunction(UInkThread* Caller, const TArray& Params) { - // Register tag function - mGlobalTagFunctions.FindOrAdd(functionName).Add(function); + FName name(*Params[0]); + auto* tokens = mTagFunctionTokens.Find(name); + auto* delegates = mTagFunctionDelegates.Find(name); + if (! tokens || ! delegates) + return; + + // Fire active delegates, compact dead entries lazily + for (int32 i = tokens->Num() - 1; i >= 0; --i) { + if ((*tokens)[i].IsValid() && *(*tokens)[i]) { + (*delegates)[i].ExecuteIfBound(Caller, Params); + } else { + tokens->RemoveAtSwap(i); + delegates->RemoveAtSwap(i); + } + } } UInkThread* @@ -275,32 +289,49 @@ void AInkRuntime::SetGlobalVariable(const FString& name, const FInkVar& value) } } -void AInkRuntime::ObserverVariable(const FString& name, const FVariableCallbackDelegate& callback) +FInkHandle + AInkRuntime::ObserverVariable(const FString& name, const FVariableCallbackDelegate& callback) { - mpGlobals->observe(TCHAR_TO_UTF8(*name), [callback]() { callback.Execute(); }); + auto token = MakeShared(true); + mObserverTokens.Add(token); + // Capture token by value; if it is set to false the callback is skipped. + // Use TWeakObjectPtr for the bound UObject inside the delegate to avoid + // keeping it alive longer than the GC would otherwise. + mpGlobals->observe(TCHAR_TO_UTF8(*name), [token, callback]() { + if (token.IsValid() && *token) { + callback.ExecuteIfBound(); + } + }); + return FInkHandle(token); } -void AInkRuntime::ObserverVariableEvent( +FInkHandle AInkRuntime::ObserverVariableEvent( const FString& name, const FVariableCallbackDelegateNewValue& callback ) { - mpGlobals->observe(TCHAR_TO_UTF8(*name), [callback](ink::runtime::value x) { - callback.Execute(FInkVar(x)); + auto token = MakeShared(true); + mObserverTokens.Add(token); + mpGlobals->observe(TCHAR_TO_UTF8(*name), [token, callback](ink::runtime::value x) { + if (token.IsValid() && *token) { + callback.ExecuteIfBound(FInkVar(x)); + } }); + return FInkHandle(token); } -void AInkRuntime::ObserverVariableChange( +FInkHandle AInkRuntime::ObserverVariableChange( const FString& name, const FVariableCallbackDelegateNewOldValue& callback ) { + auto token = MakeShared(true); + mObserverTokens.Add(token); mpGlobals->observe( TCHAR_TO_UTF8(*name), - [callback](ink::runtime::value x, ink::optional y) { - if (y.has_value()) { - callback.Execute(FInkVar(x), FInkVar(y.value())); - } else { - callback.Execute(FInkVar(x), FInkVar()); + [token, callback](ink::runtime::value x, ink::optional y) { + if (token.IsValid() && *token) { + callback.ExecuteIfBound(FInkVar(x), y.has_value() ? FInkVar(y.value()) : FInkVar()); } } ); + return FInkHandle(token); } diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index 47823424..fd77f888 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -74,24 +74,81 @@ const UTagList* UInkThread::GetGlobalTags() void UInkThread::Resume() { mnYieldCounter--; } -void UInkThread::RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function) +FInkHandle UInkThread::RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function) { - // Register tag function - mTagFunctions.FindOrAdd(functionName).Add(function); + auto token = MakeShared(true); + mTagFunctionTokens.FindOrAdd(functionName).Add(token); + mTagFunctionDelegates.FindOrAdd(functionName).Add(function); + return FInkHandle(token); } -void UInkThread::RegisterExternalFunction( +FInkHandle UInkThread::RegisterExternalFunction( const FString& functionName, const FExternalFunctionDelegate& function, bool lookaheadSafe ) { - mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_UTF8(*functionName)), function, lookaheadSafe); + auto token = MakeShared(true); + uint32 nameHash = ink::hash_string(TCHAR_TO_UTF8(*functionName)); + // If a previous binding exists for this name, invalidate it + if (auto* prev = mExternalFunctionTokens.Find(nameHash)) { + if (prev->IsValid()) { + **prev = false; + } + } + mExternalFunctionTokens.Add(nameHash, token); + // Bind a wrapper lambda that checks the token before forwarding + mpRunner->bind( + nameHash, + [token, function](size_t argc, const ink::runtime::value* argv) -> ink::runtime::value { + if (token.IsValid() && *token) { + TArray args; + for (size_t i = 0; i < argc; ++i) { + args.Add(FInkVar(argv[i])); + } + return function.Execute(args).to_value(); + } + return ink::runtime::value{}; + }, + lookaheadSafe + ); + return FInkHandle(token); } -void UInkThread::RegisterExternalEvent( +FInkHandle UInkThread::RegisterExternalEvent( const FString& functionName, const FExternalFunctionVoidDelegate& function, bool lookaheadSafe ) { - mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_UTF8(*functionName)), function, lookaheadSafe); + auto token = MakeShared(true); + uint32 nameHash = ink::hash_string(TCHAR_TO_UTF8(*functionName)); + if (auto* prev = mExternalFunctionTokens.Find(nameHash)) { + if (prev->IsValid()) { + **prev = false; + } + } + mExternalFunctionTokens.Add(nameHash, token); + mpRunner->bind( + nameHash, + [token, function](size_t argc, const ink::runtime::value* argv) { + if (token.IsValid() && *token) { + TArray args; + for (size_t i = 0; i < argc; ++i) { + args.Add(FInkVar(argv[i])); + } + function.ExecuteIfBound(args); + } + }, + lookaheadSafe + ); + return FInkHandle(token); +} + +void UInkThread::ClearExternalFunctions() +{ + for (auto& pair : mExternalFunctionTokens) { + if (pair.Value.IsValid()) { + *pair.Value = false; + } + } + mExternalFunctionTokens.Empty(); } void UInkThread::Initialize(FString path, AInkRuntime* runtime, ink::runtime::runner thread) @@ -107,7 +164,10 @@ void UInkThread::Initialize(FString path, AInkRuntime* runtime, ink::runtime::ru mpTags = NewObject(); mkTags = NewObject(); mgTags = NewObject(); - mTagFunctions.Reset(); + mTagFunctionTokens.Reset(); + mTagFunctionDelegates.Reset(); + // Note: external function tokens are intentionally NOT cleared here. + // Call ClearExternalFunctions() explicitly before reuse via StartExisting(). mCurrentChoices.Reset(); mnChoiceToChoose = -1; mbHasRun = false; @@ -246,10 +306,20 @@ bool UInkThread::ExecuteInternal() void UInkThread::ExecuteTagMethod(const TArray& Params) { - // Look for method and execute with parameters - FTagFunctionMulticastDelegate* function = mTagFunctions.Find(FName(*Params[0])); - if (function != nullptr) { - function->Broadcast(this, Params); + FName name(*Params[0]); + + // Fire thread-local tag functions + auto* tokens = mTagFunctionTokens.Find(name); + auto* delegates = mTagFunctionDelegates.Find(name); + if (tokens && delegates) { + for (int32 i = tokens->Num() - 1; i >= 0; --i) { + if ((*tokens)[i].IsValid() && *(*tokens)[i]) { + (*delegates)[i].ExecuteIfBound(this, Params); + } else { + tokens->RemoveAtSwap(i); + delegates->RemoveAtSwap(i); + } + } } // Forward to runtime diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkHandles.h b/unreal/inkcpp/Source/inkcpp/Public/InkHandles.h index 626a91e8..8f9971d0 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkHandles.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkHandles.h @@ -12,59 +12,44 @@ #include "InkHandles.generated.h" /** - * Handle returned by @ref AInkRuntime::ObserveVariable() and variants. - * Pass to @ref AInkRuntime::UnobserveVariable() to stop receiving callbacks. - * Letting the handle go out of scope does NOT automatically unregister — - * you must call UnobserveVariable() explicitly. + * Generic registration handle returned by all Register* functions on + * @ref AInkRuntime and @ref UInkThread. + * + * Call @ref Cancel() to cancel the registration. Letting the handle go out of scope does NOT + * automatically cancel — you must call Cancel() explicitly. + * For Blueprint graphs, pass the handle to @ref AInkRuntime::Unregister() or + * @ref UInkThread::Unregister() — those are thin wrappers around Cancel(). + * + * The same handle type is used for variable observers, tag functions, + * and external functions — the registering function name already makes + * the context clear at the call site. + * * @ingroup unreal */ USTRUCT(BlueprintType) -struct INKCPP_API FInkObserverHandle { +struct INKCPP_API FInkHandle { GENERATED_BODY() /** @private */ - FInkObserverHandle() {} + FInkHandle() {} /** @private */ - explicit FInkObserverHandle(TSharedPtr token) + explicit FInkHandle(TSharedPtr token) : Token(MoveTemp(token)) { } - /** Returns true if this handle refers to an active observer registration. */ + /** Returns true if this handle refers to an active registration. */ bool IsValid() const { return Token.IsValid() && *Token; } - /** @private */ - TSharedPtr Token; -}; - -/** - * Handle returned by @ref UInkThread::RegisterExternalFunction() and - * @ref UInkThread::RegisterExternalEvent(). - * Pass to @ref UInkThread::UnregisterExternalFunction() to remove the binding. - * @ingroup unreal - */ -USTRUCT(BlueprintType) - -struct INKCPP_API FExternalFunctionHandle { - GENERATED_BODY() - - /** @private */ - FExternalFunctionHandle() {} - - /** @private */ - explicit FExternalFunctionHandle(TSharedPtr token, FString name) - : Token(MoveTemp(token)) - , FunctionName(MoveTemp(name)) + /** Cancels the registration this handle refers to. Safe to call multiple times. */ + void Cancel() const { + if (Token.IsValid()) + *Token = false; } - /** Returns true if this handle refers to an active external function registration. */ - bool IsValid() const { return Token.IsValid() && *Token; } - /** @private */ TSharedPtr Token; - /** @private */ - FString FunctionName; }; diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h index f34cc39f..42d8b056 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h @@ -96,12 +96,24 @@ class INKCPP_API AInkRuntime : public AActor UFUNCTION(BlueprintCallable, Category = "Ink") /** register a "tag function" - * This function is executed if context or a tag in a special format appears + * This function is executed if context or a tag in a special format appears. + * @return handle — call Cancel() to remove this binding (or pass to Unregister() from Blueprint) * @see @ref TagFunction * * @blueprint */ - void RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function); + FInkHandle RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function); + + UFUNCTION(BlueprintCallable, Category = "Ink") + + /** Stop receiving variable-change notifications or unregister a tag function. + * Prefer calling @ref FInkHandle::Cancel() directly — that does not require the runtime. + * @param handle the handle returned by ObserverVariable / ObserverVariableEvent / + * ObserverVariableChange / RegisterTagFunction + * + * @blueprint + */ + void Unregister(const FInkHandle& handle) { handle.Cancel(); } /** @private for internal use */ void HandleTagFunction(UInkThread* Caller, const TArray& Params); @@ -126,47 +138,38 @@ class INKCPP_API AInkRuntime : public AActor UFUNCTION(BlueprintCallable, Category = "Ink") /** Gets a ping if variable changes. - * @return handle — pass to UnobserveVariable() to stop receiving callbacks + * @return handle — call Cancel() to remove this binding (or pass to Unregister() from Blueprint) * @see #ObserverVariableEvent() #ObserverVariableChange() * * @blueprint */ - FInkObserverHandle + FInkHandle ObserverVariable(const FString& variableName, const FVariableCallbackDelegate& callback); UFUNCTION(BlueprintCallable, Category = "Ink") /** On variable change provides new value. - * @return handle — pass to UnobserveVariable() to stop receiving callbacks + * @return handle — call Cancel() to remove this binding (or pass to Unregister() from Blueprint) * @see #ObserverVariable() #ObserverVariableChange() * * @blueprint */ - FInkObserverHandle ObserverVariableEvent( + FInkHandle ObserverVariableEvent( const FString& variableName, const FVariableCallbackDelegateNewValue& callback ); UFUNCTION(BlueprintCallable, Category = "Ink") /** On variable change provides old and new value. - * @return handle — pass to UnobserveVariable() to stop receiving callbacks + * @return handle — call Cancel() to remove this binding (or pass to Unregister() from Blueprint) * @attention if the variable set for the first time, the old value has value type @ref * EInkVarType::None * @see #ObserverVariableEvent() #ObserverVariable() * * @blueprint */ - FInkObserverHandle ObserverVariableChange( + FInkHandle ObserverVariableChange( const FString& variableName, const FVariableCallbackDelegateNewOldValue& callback ); - UFUNCTION(BlueprintCallable, Category = "Ink") - /** Stop receiving variable-change notifications for a previously registered observer. - * @param handle the handle returned by ObserverVariable / ObserverVariableEvent / - * ObserverVariableChange - * - * @blueprint - */ - void UnobserveVariable(const FInkObserverHandle& handle); - protected: /** Called when the game starts or when spawned */ virtual void BeginPlay() override; @@ -191,7 +194,10 @@ class INKCPP_API AInkRuntime : public AActor UPROPERTY() TArray mThreads; - TMap mGlobalTagFunctions; + /** Token storage for tag function registrations. Maps function name → parallel arrays of + * tokens and delegates. Setting a token to false skips and lazily removes that entry. */ + TMap>> mTagFunctionTokens; + TMap> mTagFunctionDelegates; UPROPERTY() TArray mExclusiveStack; @@ -200,7 +206,7 @@ class INKCPP_API AInkRuntime : public AActor TOptional mSnapshot; ink::runtime::snapshot* mpSnapshot = nullptr; - /** Active observer tokens. When UnobserveVariable() is called the token is set to false, + /** Active observer tokens. When Cancel() is called on the handle the token is set to false, * the lambda checks it before firing and skips. Tokens are cleaned up lazily. */ TArray> mObserverTokens; }; diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h index 0fd749ba..5e493373 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h @@ -12,6 +12,7 @@ #include "InkVar.h" #include "InkDelegates.h" #include "InkList.h" +#include "InkHandles.h" #include "ink/runner.h" @@ -148,37 +149,56 @@ class INKCPP_API UInkThread : public UObject // Registers a callback for a named "tag function" UFUNCTION(BlueprintCallable, Category = "Ink") - /** Register a callback for a named "tag function" - * @see @ref TagFunction + /** Register a callback for a named "tag function". + * @return handle — call Cancel() to remove this binding (or pass to Unregister() from Blueprint) * * @blueprint */ - void RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function); + FInkHandle RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function); UFUNCTION(BlueprintCallable, Category = "Ink") - /** register a external function. - * A function provides a return value + /** Register an external function that returns a value. + * @return handle — call Cancel() to remove this binding (or pass to Unregister() from Blueprint) * @see if you do not want to return something #RegisterExternalEvent() * * @blueprint */ - void RegisterExternalFunction( + FInkHandle RegisterExternalFunction( const FString& functionName, const FExternalFunctionDelegate& function, bool lookaheadSafe = false ); UFUNCTION(BlueprintCallable, Category = "Ink") - /** register external event. - * A event has the return type void. - * @see If you want to return a value use #RegisterExternalFunction() + /** Register an external event (void return). + * @return handle — call Cancel() to remove this binding (or pass to Unregister() from Blueprint) + * @see If you want to return a value use #RegisterExternalFunction() * * @blueprint */ - void RegisterExternalEvent( + FInkHandle RegisterExternalEvent( const FString& functionName, const FExternalFunctionVoidDelegate& function, bool lookaheadSafe = false ); + UFUNCTION(BlueprintCallable, Category = "Ink") + + /** Unregister a previously registered external function, event, or tag function. + * Prefer calling @ref FInkHandle::Cancel() directly — that does not require the thread. + * @param handle the handle returned by RegisterExternalFunction() / RegisterExternalEvent() / + * RegisterTagFunction() + * + * @blueprint + */ + void Unregister(const FInkHandle& handle) { handle.Cancel(); } + + UFUNCTION(BlueprintCallable, Category = "Ink") + /** Unregister all external functions and events bound to this thread. + * Useful when reusing a thread via StartExisting() to ensure no stale bindings remain. + * + * @blueprint + */ + void ClearExternalFunctions(); + UFUNCTION(BlueprintCallable, Category = "Ink") /** get knots assoziated with current knot. * knot tags are tags listed behind a knot `== knot name ==` before the first line of content @@ -264,7 +284,17 @@ class INKCPP_API UInkThread : public UObject /** Lists wrapping runner-owned memory, registered during current execute cycle. */ TArray> mLiveLists; - TMap mTagFunctions; + /** Token storage for tag function registrations. Maps function name → parallel arrays of + * tokens and delegates. Each token is shared with the corresponding FInkHandle. + * Setting a token to false causes the entry to be skipped and lazily compacted. */ + TMap>> mTagFunctionTokens; + + /** Multicast delegates for tag functions, parallel to mTagFunctionTokens. */ + TMap> mTagFunctionDelegates; + + /** Token storage for external function registrations, keyed by name hash. + * A matching FInkHandle invalidates the token to suppress the binding. */ + TMap> mExternalFunctionTokens; FString mStartPath; bool mbHasRun; From 51348e7fc05516e44eaa80e8fedb06d5753a446e Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 29 May 2026 21:05:19 +0200 Subject: [PATCH 05/29] fix(UE): fix UE 5.7 compile errors --- inkcpp/globals_impl.cpp | 2 +- inkcpp/include/functional.h | 3 ++- inkcpp/stack.cpp | 2 +- inkcpp_compiler/binary_emitter.cpp | 4 ++-- inkcpp_python/pybind11 | 2 +- shared/public/system.h | 8 ++++++- .../Source/inkcpp/Private/InkRuntime.cpp | 8 +++---- .../Source/inkcpp/Private/InkThread.cpp | 6 ++--- .../inkcpp/Source/inkcpp/Private/InkVar.cpp | 23 +++++++++++++------ .../inkcpp/Source/inkcpp/Public/InkThread.h | 2 ++ unreal/inkcpp/Source/inkcpp/Public/InkVar.h | 7 +----- 11 files changed, 40 insertions(+), 27 deletions(-) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 45efa0aa..af8e96b4 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -303,7 +303,7 @@ const unsigned char* globals_impl::snap_load(const unsigned char* ptr, const loa for (size_t i = 0; i < old_capacity; ++i) { hash_t path; ptr = snap_read(ptr, path); - container_t c_id; + container_t c_id = ~0U; ip_t container_ip = _owner->find_offset_for(path); bool found = container_ip != nullptr && _owner->find_container_id( diff --git a/inkcpp/include/functional.h b/inkcpp/include/functional.h index 11b577a4..db354121 100644 --- a/inkcpp/include/functional.h +++ b/inkcpp/include/functional.h @@ -204,8 +204,9 @@ class function : public function_base if constexpr (traits::arity == 2) { return is_same< const ink::runtime::value*, typename traits::template argument<1>::type>::value; + } else { + return false; } - return false; } template diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index 97192b87..3be83512 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -591,7 +591,7 @@ bool basic_stack::can_be_migrated() const bool basic_stack::migrate(basic_stack& new_stack) { - inkAssert(can_be_migrated() && new_stack.can_be_migrated()); + inkAssert(can_be_migrated() && new_stack.can_be_migrated(), "Unable to migrate this stack."); // move existing values to new_stack, iff there the variable is also in the new stack for_each_all([&new_stack](const entry& e) { const value* oth = new_stack.get(e.name); diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index 1f9e3e1c..594c3600 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -229,7 +229,7 @@ void binary_emitter::emit_section(std::ostream& stream, const std::vector& void binary_emitter::emit_section(std::ostream& stream, const binary_stream& data) const { - inkAssert((stream.tellp() & (ink::internal::header::Alignment - 1)) == 0); + inkAssert((stream.tellp() & (ink::internal::header::Alignment - 1)) == 0, "The stream is missaligned"); data.write_to(stream); close_section(stream); } @@ -449,7 +449,7 @@ void binary_emitter::build_container_data( d._start_offset = context->offset; d._end_offset = context->end_offset; const uint8_t flags = _instructions.get(context->offset + 1); - inkAssert(flags < 16); + inkAssert(flags < 16, "Flags exceed the lower nibbel!"); d._flags = flags; // Since we might be skipping tree levels, we need to be explicit about the parent. diff --git a/inkcpp_python/pybind11 b/inkcpp_python/pybind11 index 1b499083..0c69e1eb 160000 --- a/inkcpp_python/pybind11 +++ b/inkcpp_python/pybind11 @@ -1 +1 @@ -Subproject commit 1b4990838904501de7110d27e96c0a4152029156 +Subproject commit 0c69e1eb2177fa8f8580632c7b1f97fdb606ce8f diff --git a/shared/public/system.h b/shared/public/system.h index 196897ea..d2ae0b12 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -133,6 +133,12 @@ inline hash_t hash_string(const char* string) { return CityHash32(string, FCStringAnsi::Strlen(string)); } + +/** Simple hash for detcting changes in binary data. (e.g. Changes in the story file) */ +inline hash_t hash_data(const unsigned char* data, size_t len) +{ + return CityHash32(reinterpret_cast(data), len); +} #else hash_t hash_string(const char* string); hash_t hash_data(const unsigned char* data, size_t len); @@ -259,7 +265,7 @@ void ink_assert(bool condition, const char* msg = nullptr, Args... args) fprintf(stderr, "Ink Assert: %s\n", msg); abort(); #elif defined(INK_ENABLE_UNREAL) - // TODO: implement UE exception handling + // TODO: implement UE exception handling #else # warning no assertion handling this could lead to invalid code paths #endif diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index 2e8646fd..976d8775 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -134,7 +134,7 @@ void AInkRuntime::Tick(float DeltaTime) FInkHandle AInkRuntime::RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function) { - auto token = MakeShared(true); + TSharedPtr token = MakeShared(true); mTagFunctionTokens.FindOrAdd(functionName).Add(token); mTagFunctionDelegates.FindOrAdd(functionName).Add(function); mObserverTokens.Add(token); @@ -292,7 +292,7 @@ void AInkRuntime::SetGlobalVariable(const FString& name, const FInkVar& value) FInkHandle AInkRuntime::ObserverVariable(const FString& name, const FVariableCallbackDelegate& callback) { - auto token = MakeShared(true); + TSharedPtr token = MakeShared(true); mObserverTokens.Add(token); // Capture token by value; if it is set to false the callback is skipped. // Use TWeakObjectPtr for the bound UObject inside the delegate to avoid @@ -309,7 +309,7 @@ FInkHandle AInkRuntime::ObserverVariableEvent( const FString& name, const FVariableCallbackDelegateNewValue& callback ) { - auto token = MakeShared(true); + TSharedPtr token = MakeShared(true); mObserverTokens.Add(token); mpGlobals->observe(TCHAR_TO_UTF8(*name), [token, callback](ink::runtime::value x) { if (token.IsValid() && *token) { @@ -323,7 +323,7 @@ FInkHandle AInkRuntime::ObserverVariableChange( const FString& name, const FVariableCallbackDelegateNewOldValue& callback ) { - auto token = MakeShared(true); + TSharedPtr token = MakeShared(true); mObserverTokens.Add(token); mpGlobals->observe( TCHAR_TO_UTF8(*name), diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index fd77f888..d4949c99 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -76,7 +76,7 @@ void UInkThread::Resume() { mnYieldCounter--; } FInkHandle UInkThread::RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function) { - auto token = MakeShared(true); + TSharedPtr token = MakeShared(true); mTagFunctionTokens.FindOrAdd(functionName).Add(token); mTagFunctionDelegates.FindOrAdd(functionName).Add(function); return FInkHandle(token); @@ -86,7 +86,7 @@ FInkHandle UInkThread::RegisterExternalFunction( const FString& functionName, const FExternalFunctionDelegate& function, bool lookaheadSafe ) { - auto token = MakeShared(true); + TSharedPtr token = MakeShared(true); uint32 nameHash = ink::hash_string(TCHAR_TO_UTF8(*functionName)); // If a previous binding exists for this name, invalidate it if (auto* prev = mExternalFunctionTokens.Find(nameHash)) { @@ -117,7 +117,7 @@ FInkHandle UInkThread::RegisterExternalEvent( const FString& functionName, const FExternalFunctionVoidDelegate& function, bool lookaheadSafe ) { - auto token = MakeShared(true); + TSharedPtr token = MakeShared(true); uint32 nameHash = ink::hash_string(TCHAR_TO_UTF8(*functionName)); if (auto* prev = mExternalFunctionTokens.Find(nameHash)) { if (prev->IsValid()) { diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp index 12e78883..d147b9dc 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp @@ -6,15 +6,12 @@ */ #include "InkVar.h" #include "InkExecutionScope.h" +#include "InkThread.h" #include "ink/types.h" #include "Misc/AssertionMacros.h" -#include "InkVar.h" -#include "InkExecutionScope.h" -#include "ink/types.h" - -#include "Misc/AssertionMacros.h" +extern thread_local UInkThread* GExecutingInkThread; FInkVar::FInkVar(ink::runtime::value val) : FInkVar() @@ -28,8 +25,10 @@ FInkVar::FInkVar(ink::runtime::value val) case v_types::Uint32: UE_LOG( InkCpp, Warning, - TEXT("Converting uint to int, this will cause trouble if writing it back to ink (with " - "SetGlobalVariable)!") + TEXT( + "Converting uint to int, this will cause trouble if writing it back to ink (with " + "SetGlobalVariable)!" + ) ); IntVal = ( int32 ) val.get(); VarType = EInkVarType::Int; @@ -64,6 +63,16 @@ FInkVar::FInkVar(ink::runtime::value val) } } +FInkVar::FInkVar(UInkList& List) + : VarType(EInkVarType::List) + , IntVal(0) + , ListVal(&List) +{ + if (GExecutingInkThread) { + GExecutingInkThread->RegisterLiveList(ListVal); + } +} + ink::runtime::value FInkVar::to_value() const { switch (VarType) { diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h index 5e493373..53bad52b 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h @@ -7,6 +7,7 @@ #pragma once #include "CoreMinimal.h" +#include "InkRuntime.h" #include "UObject/NoExportTypes.h" #include "InkVar.h" @@ -259,6 +260,7 @@ class INKCPP_API UInkThread : public UObject * @private */ void RegisterLiveList(UInkList* list); + friend FInkVar::FInkVar(UInkList&), FInkVar::FInkVar(ink::runtime::value); /** Invalidate all UInkList objects registered since the last choose(). * Called internally before mpRunner->choose(). diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h index 52d3439a..c1893ed6 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h @@ -93,12 +93,7 @@ struct INKCPP_API FInkVar { } /** @private */ - FInkVar(UInkList& List) - : VarType(EInkVarType::List) - , IntVal(0) - , ListVal(&List) - { - } + FInkVar(UInkList& List); /** @private */ FInkVar(ink::runtime::value val); From 20378e0dbf95956025942cc721efcd76aa2ce197 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Sat, 30 May 2026 00:26:08 +0200 Subject: [PATCH 06/29] feat(UE): Allow migratable snapshots with sync snapshot taking --- .../Source/inkcpp/Private/InkRuntime.cpp | 77 ++++++++++++++++++- .../Source/inkcpp/Private/InkThread.cpp | 1 + .../inkcpp/Source/inkcpp/Public/InkRuntime.h | 27 ++++++- .../inkcpp/Source/inkcpp/Public/InkSnapshot.h | 42 +++++++++- 4 files changed, 140 insertions(+), 7 deletions(-) diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index 976d8775..4c9f080e 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -6,6 +6,8 @@ */ #include "InkRuntime.h" +#include "Async/Async.h" + // Game includes #include "inkcpp.h" #include "InkThread.h" @@ -19,6 +21,7 @@ #include "ink/snapshot.h" #include "system.h" #include "types.h" +#include namespace ink { @@ -180,12 +183,51 @@ FInkSnapshot AInkRuntime::Snapshot() { ink::runtime::snapshot* inkSnapshot = mpGlobals->create_snapshot(); FInkSnapshot snapshot( - reinterpret_cast(inkSnapshot->get_data()), inkSnapshot->get_data_len() + reinterpret_cast(inkSnapshot->get_data()), inkSnapshot->get_data_len(), + inkSnapshot->can_be_migrated() ); delete inkSnapshot; return snapshot; } +TFuture AInkRuntime::MigratableSnapshot() +{ + // Fast path: already stable + FInkSnapshot snapshot = Snapshot(); + + if (snapshot.Migratable) + { + TPromise Immediate; + Immediate.SetValue(snapshot); + return Immediate.GetFuture(); + } + + // Slow path: wait for stability + TSharedRef, ESPMode::ThreadSafe> Promise = + MakeShared, ESPMode::ThreadSafe>(); + + mStableSnapshot = Promise; + + return Promise->GetFuture(); +} + +void AInkRuntime::RunnerEnterStableState(UInkThread* thread) +{ + if (mStableSnapshot.IsValid()) { + thread->Yield(); + mYieldedThreadsForSnapshot.Add(thread); + FInkSnapshot snapshot = Snapshot(); + if (snapshot.Migratable) { + mStableSnapshot->SetValue(snapshot); + mStableSnapshot.Reset(); + for (auto& _thread : mYieldedThreadsForSnapshot) { + _thread->Resume(); + } + mYieldedThreadsForSnapshot.Empty(); + } + } +} + void AInkRuntime::LoadSnapshot(const FInkSnapshot& snapshot) { if (mpSnapshot) { @@ -197,6 +239,18 @@ void AInkRuntime::LoadSnapshot(const FInkSnapshot& snapshot) reinterpret_cast(mSnapshot->data.GetData()), mSnapshot->data.Num(), false ); mpGlobals = mpRuntime->new_globals_from_snapshot(*mpSnapshot); + if (! mpGlobals.is_valid()) { + UE_LOG(InkCpp, Error, TEXT("Failed to load snapshot.")); + if (! mpSnapshot->can_be_migrated()) { + UE_LOG( + InkCpp, Error, + TEXT( + "Unable to load snapshot. The story has changed and the snapshot was taken at in " + "instable moment." + ) + ); + } + } } UInkThread* @@ -207,6 +261,18 @@ UInkThread* return nullptr; } + if (! mpGlobals.is_valid()) { + if (mSnapshot) { + UE_LOG( + InkCpp, Error, + TEXT("Failed to start existing, due to invalid state after failed snapshot loading.") + ); + } else { + UE_LOG(InkCpp, Warning, TEXT("Failed to start existing")); + } + return nullptr; + } + // remove handle if it still exists mThreads.Remove(thread); mExclusiveStack.Remove(thread); @@ -217,8 +283,10 @@ UInkThread* if (mpSnapshot->num_runners() == mThreads.Num()) { UE_LOG( InkCpp, Warning, - TEXT("Already created all Threads from Snapshot!, will not create more. You can Still " - "create new Threads with entering the starting Path.") + TEXT( + "Already created all Threads from Snapshot!, will not create more. You can Still " + "create new Threads with entering the starting Path." + ) ); return nullptr; } @@ -230,7 +298,8 @@ UInkThread* // If we're not starting immediately, just queue if (! startImmediately || - // Even if we want to start immediately, don't if there's an exclusive thread and it's not us + // Even if we want to start immediately, don't if there's an exclusive thread and it's not + // us (mExclusiveStack.Num() > 0 && mExclusiveStack.Top() != thread)) { mThreads.Add(thread); return thread; diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index d4949c99..20b3782a 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -211,6 +211,7 @@ bool UInkThread::ExecuteInternal() // before the runner state changes. InvalidateLiveLists(); mpRunner->choose(mnChoiceToChoose); + mpRuntime->RunnerEnterStableState(this); } mnChoiceToChoose = -1; mCurrentChoices.Empty(); diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h index 42d8b056..fff1eae8 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h @@ -9,6 +9,7 @@ #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "Misc/Optional.h" +#include "Async/Future.h" #include "InkDelegates.h" #include "InkSnapshot.h" @@ -61,12 +62,31 @@ class INKCPP_API AInkRuntime : public AActor UFUNCTION(BlueprintCallable, Category = "Ink") /** creates a snapshot of the current runtime state. - * can be loladed with @ref #LoadSnapshot() + * can be loaded with @ref #LoadSnapshot() + * + * @attention this snapshot can only be loaded with the same story, for migratable snapshots + * please use UInkMigratableSnapshotAsync::UInkMigratableSnapshotAsync() + * + * make snapshot, if save? Return + * every time a thread chooses something + * yield this thread + * try to make snapshot, if save? fullfill promise and resume all threads * * @blueprint */ FInkSnapshot Snapshot(); + /** creates a snapshot the next time the story is in a stable state. + * for Blueprints please use snapshotAsync::UInkMigratableSnapshotAsync() + * This snapshot can be loaded with a new version of the same story. + * can be loaded with @ref #LoadSnapshot() + * + * @attention typical this snapshot will be created after the next choice is taken. + * To archive this each active runner will yield after the next choice and only continue after the snapshot is taken. + * + */ + TFuture MigratableSnapshot(); + UFUNCTION(BlueprintCallable, Category = "Ink") /** * Loads a snapshot file, therefore deletes globals and invalidate all current Threads @@ -118,6 +138,9 @@ class INKCPP_API AInkRuntime : public AActor /** @private for internal use */ void HandleTagFunction(UInkThread* Caller, const TArray& Params); + /** @private for internal use */ + void RunnerEnterStableState(UInkThread* thread); + UFUNCTION(BlueprintCallable, Category = "Ink") /** Access a variable from the ink runtime. * variables are shared between all threads in the same runtime. @@ -209,4 +232,6 @@ class INKCPP_API AInkRuntime : public AActor /** Active observer tokens. When Cancel() is called on the handle the token is set to false, * the lambda checks it before firing and skips. Tokens are cleaned up lazily. */ TArray> mObserverTokens; + TSharedPtr> mStableSnapshot; + TArray mYieldedThreadsForSnapshot; }; diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h b/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h index bb7d6d7c..6da7bf0a 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h @@ -6,6 +6,8 @@ */ #pragma once +#include "Kismet/BlueprintAsyncActionBase.h" + #include "InkSnapshot.generated.h" /** A serializable snapshot of a runtime state @@ -19,12 +21,48 @@ struct INKCPP_API FInkSnapshot FInkSnapshot() {} /** @private */ - FInkSnapshot(const char* snap_data, size_t snap_len) - : data(reinterpret_cast(snap_data), snap_len) + FInkSnapshot(const char* snap_data, size_t snap_len, bool migratable) + : data(reinterpret_cast(snap_data), snap_len), + Migratable(migratable) {} UPROPERTY(BlueprintReadWrite, SaveGame, Category = "ink|SaveGame") /** Raw data used to restore runtime state. * not needed if a USaveGame is used. */ TArray data; + + UPROPERTY(BlueprintReadOnly, SaveGame, Category = "ink|SaveGame") + /** Is true if the snapshot is migratable. + */ + bool Migratable; +}; + + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( + FInkMigratableSnapshotCompleted, + const FInkSnapshot&, Snapshot +); + +UCLASS() +class INKCPP_API UInkMigratableSnapshotAsync : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FInkMigratableSnapshotCompleted Completed; + + UFUNCTION(BlueprintCallable, + meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject")) + static UInkMigratableSnapshotAsync* GetMigratableSnapshot( + AInkRuntime* Runtime); + + virtual void Activate() override; + +private: + UPROPERTY() + TObjectPtr Runtime; + + void HandleResult(const FInkSnapshot& Snapshot); }; From 6e342b963758664fe0910ac112df4720e622d732 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Sat, 30 May 2026 23:32:50 +0200 Subject: [PATCH 07/29] fix(globals): migration can also reduce the number of knots -> resize of allocated_resizable_array must be work for decreasing --- inkcpp/array.h | 68 +++++++++++++++---- inkcpp/container_operations.cpp | 1 + inkcpp/globals_impl.cpp | 1 + inkcpp/include/list.h | 6 +- inkcpp/include/runner.h | 19 +++++- inkcpp/runner_impl.cpp | 34 ++++++---- inkcpp/snapshot_interface.h | 7 +- inkcpp/story_impl.cpp | 11 ++- inkcpp_c/include/inkcpp.h | 11 +++ inkcpp_c/inkcpp.cpp | 2 +- inkcpp_compiler/binary_emitter.cpp | 8 +-- inkcpp_compiler/emitter.cpp | 2 +- inkcpp_compiler/emitter.h | 4 +- inkcpp_compiler/json_compiler.h | 2 +- inkcpp_test/CMakeLists.txt | 4 +- shared/public/system.h | 12 ++-- .../Source/inkcpp/Private/InkRuntime.cpp | 7 +- .../inkcpp/Source/inkcpp/Public/InkRuntime.h | 1 + .../inkcpp/Source/inkcpp/Public/InkSnapshot.h | 6 +- 19 files changed, 143 insertions(+), 63 deletions(-) diff --git a/inkcpp/array.h b/inkcpp/array.h index 5eba1c88..87f8137d 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -346,13 +346,16 @@ class basic_restorable_array : public snapshot_interface void clear(const T& value); // snapshot interface - virtual bool can_be_migrated() const; - virtual size_t snap(unsigned char* data, const snapper&) const; - virtual const unsigned char* snap_load(const unsigned char* data, const loader&); + bool can_be_migrated() const; + size_t snap(unsigned char* data, const snapper&) const; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) = 0; protected: inline T* buffer() { return _array; } + const unsigned char* impl_snap_load_meta(const unsigned char* data); + const unsigned char* impl_snap_load_payload(const unsigned char* data); + void set_new_buffer(T* buffer, size_t capacity) { _array = buffer; @@ -480,6 +483,8 @@ inline void basic_restorable_array::clear(const T& value) template class fixed_restorable_array : public basic_restorable_array { + using base = basic_restorable_array; + public: fixed_restorable_array(const T& initial, const T& nullValue) : basic_restorable_array(_buffer, SIZE * 2, nullValue) @@ -487,6 +492,9 @@ class fixed_restorable_array : public basic_restorable_array basic_restorable_array::clear(initial); } + const unsigned char* + snap_load(const unsigned char* data, const snapshot_interface::loader&) override; + private: T _buffer[SIZE * 2]; }; @@ -519,7 +527,7 @@ class allocated_restorable_array : public basic_restorable_array size_t new_capacity = 2 * n; T* new_buffer = new T[new_capacity]; if (_buffer) { - for (size_t i = 0; i < base::capacity(); ++i) { + for (size_t i = 0; i < base::capacity() && i < n; ++i) { new_buffer[i] = _buffer[i]; // copy temp new_buffer[i + n] = _buffer[i + base::capacity()]; @@ -543,6 +551,9 @@ class allocated_restorable_array : public basic_restorable_array } } + const unsigned char* + snap_load(const unsigned char* data, const snapshot_interface::loader&) override; + private: T _initialValue; T _nullValue; @@ -572,25 +583,56 @@ inline size_t basic_restorable_array::snap(unsigned char* data, const snapper template inline const unsigned char* - basic_restorable_array::snap_load(const unsigned char* data, const loader&) + basic_restorable_array::impl_snap_load_meta(const unsigned char* data) { auto ptr = data; ptr = snap_read(ptr, _saved); ptr = snap_read(ptr, _loaded_capacity); - if (buffer() == nullptr) { - static_cast&>(*this).resize(_loaded_capacity); - } - inkAssert( - _capacity >= _loaded_capacity, - "New config does not allow for necessary size used by this snapshot!" - ); T null; ptr = snap_read(ptr, null); inkAssert(null == _null, "null value is different to snapshot!"); - for (size_t i = 0; i < _loaded_capacity; ++i) { + return ptr; +} + +template +inline const unsigned char* + basic_restorable_array::impl_snap_load_payload(const unsigned char* data) +{ + inkAssert( + capacity() >= loaded_capacity(), + "New config does not allow for necessary size used by this snapshot!" + ); + auto ptr = data; + for (size_t i = 0; i < loaded_capacity(); ++i) { ptr = snap_read(ptr, _array[i]); ptr = snap_read(ptr, _temp[i]); } return ptr; } + +template +inline const unsigned char* fixed_restorable_array::snap_load( + const unsigned char* data, const snapshot_interface::loader& +) +{ + auto ptr = data; + ptr = base::impl_snap_load_meta(ptr); + ptr = base::impl_snap_load_payload(ptr); + return ptr; +} + +template +inline const unsigned char* allocated_restorable_array::snap_load( + const unsigned char* data, const snapshot_interface::loader& +) +{ + auto ptr = data; + ptr = base::impl_snap_load_meta(ptr); + if (base::buffer() == nullptr || base::capacity() < base::loaded_capacity()) { + resize(base::loaded_capacity()); + } + ptr = base::impl_snap_load_payload(ptr); + return ptr; +} + } // namespace ink::runtime::internal diff --git a/inkcpp/container_operations.cpp b/inkcpp/container_operations.cpp index 6b7aa6d5..6ff5af0c 100644 --- a/inkcpp/container_operations.cpp +++ b/inkcpp/container_operations.cpp @@ -40,6 +40,7 @@ void operation::operator()( basic_eval_stack& stack, value* vals ) { + (void)vals; stack.push(value{}.set(static_cast(_runner.num_choices()))); } diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index af8e96b4..90bc36e0 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -320,6 +320,7 @@ const unsigned char* globals_impl::snap_load(const unsigned char* ptr, const loa } if (loader.migratable) { _visit_counts.forget(); + _visit_counts.resize(_num_containers); } inkAssert( _num_containers == _visit_counts.capacity(), diff --git a/inkcpp/include/list.h b/inkcpp/include/list.h index c1354dcc..d7fecad1 100644 --- a/inkcpp/include/list.h +++ b/inkcpp/include/list.h @@ -82,6 +82,7 @@ class list_interface public: iterator(const iterator&) = default; + /** contains flag data */ struct Flag { const char* flag_name; ///< name of the flag @@ -128,9 +129,8 @@ class list_interface # pragma GCC diagnostic ignored "-Wunused-parameter" #else # pragma warning(push) -# pragma warning( \ - disable : 4100, justification : "non functional prototypes do not need the argument." \ - ) +// non functional prototypes do not need the argument. +# pragma warning(disable : 4100) #endif /** checks if a flag is contained in the list */ diff --git a/inkcpp/include/runner.h b/inkcpp/include/runner.h index c48a8797..59e3ece5 100644 --- a/inkcpp/include/runner.h +++ b/inkcpp/include/runner.h @@ -54,16 +54,31 @@ class runner_interface virtual void set_rng_seed(uint32_t seed) = 0; /** - * Moves the runner to the specified path + * Moves the runner to the specified path. + * + * @sa move_to(const char*) for more conviance * * Clears any execution context and moves the runner - * to the content at the specified path. + * to the content at the specified path. * * @param path path to search and move execution to * @return If the path was found */ virtual bool move_to(hash_t path) = 0; + /** + * Moves the runner to the specified path. + * + * Clears any execution context and moves the runner + * to the content at the specified path. + * + * @param path path to search and move execution to + * @return If the path was found + */ + bool move_to(const char* path) { + return move_to(ink::hash_string(path)); + } + /** * Can the runner continue? * diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 91b14919..90ec2143 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -340,7 +340,7 @@ void runner_impl::jump(ip_t dest, bool record_visits, bool track_knot_visit) _ptr = dest; // Find the container at or before dest, which will become the top of the post-jump stack. - const uint32_t dest_offset = dest - _story->instructions(); + const uint32_t dest_offset = static_cast(dest - _story->instructions()); const container_t dest_id = _story->find_container_for(dest_offset); // If there's no destination container, stop. @@ -484,7 +484,7 @@ runner_impl::runner_impl(const story_impl* data, globals global) , _evaluation_mode{false} , _choices() , _tags_begin(0, ~0) - , _container(~0) + , _container(~0U) #ifdef INK_ENABLE_CSTD , _rng(static_cast(time(NULL))) #else @@ -662,7 +662,8 @@ bool runner_impl::can_be_migrated() const ? _story->find_container_for(static_cast(_ptr - _story->instructions() - 6)) : ~0U; hash_t c_hash = (container_id != ~0U) ? _story->container_data(container_id)._hash : 0; - if (c_hash == 0) { + // if we are not at the start or terimanet bu we cannot name the current position it is not migratble + if (c_hash == 0 && _ptr != nullptr && _ptr != _story->instructions()) { return false; } return _output.can_be_migrated() && _stack.can_be_migrated() && _ref_stack.can_be_migrated() @@ -910,7 +911,8 @@ bool runner_impl::line_step() _entered_global = false; } else if (_entered_knot) { if (has_knot_tags()) { - clear_tags(tags_clear_level::KEEP_GLOBAL_AND_UNKNOWN + clear_tags( + tags_clear_level::KEEP_GLOBAL_AND_UNKNOWN ); // clear knot tags since whe are entering another knot } @@ -1445,9 +1447,11 @@ void runner_impl::step() // Load value from output stream // Push onto stack - _eval.push(value{}.set( - _output.get_alloc(_globals->strings(), _globals->lists()) - )); + _eval.push( + value{}.set( + _output.get_alloc(_globals->strings(), _globals->lists()) + ) + ); } break; // == Tag commands @@ -1593,9 +1597,11 @@ void runner_impl::step() read(); // Push the visit count for the current container to the top // is 0-indexed for some reason. idk why but this is what ink expects - _eval.push(value{}.set( - static_cast(_globals->visits(_container.top()) - 1) - )); + _eval.push( + value{}.set( + static_cast(_globals->visits(_container.top()) - 1) + ) + ); } break; case Command::TURN: { read(); @@ -1612,7 +1618,8 @@ void runner_impl::step() _eval.pop(); - _eval.push(value{}.set(static_cast(_rng.rand(sequenceLength))) + _eval.push( + value{}.set(static_cast(_rng.rand(sequenceLength))) ); } break; case Command::SEED: { @@ -1634,8 +1641,9 @@ void runner_impl::step() container_t container = read(); // Push the read count for the requested container index - _eval.push(value{}.set(static_cast(_globals->visits(container) - ))); + _eval.push( + value{}.set(static_cast(_globals->visits(container))) + ); } break; case Command::TAG: { read(); diff --git a/inkcpp/snapshot_interface.h b/inkcpp/snapshot_interface.h index ced1f112..d47e15e0 100644 --- a/inkcpp/snapshot_interface.h +++ b/inkcpp/snapshot_interface.h @@ -22,7 +22,7 @@ class value; class snapshot_interface { public: - constexpr snapshot_interface(){}; + constexpr snapshot_interface() {}; static unsigned char* snap_write(unsigned char* ptr, const void* data, size_t length, bool write) { @@ -90,9 +90,8 @@ class snapshot_interface # pragma GCC diagnostic ignored "-Wunused-parameter" #else # pragma warning(push) -# pragma warning( \ - disable : 4100, justification : "non functional prototypes do not need the argument." \ - ) +// non functional prototypes do not need the argument. +# pragma warning(disable : 4100) #endif size_t snap(unsigned char* data, snapper&) const diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index 8c3c9e2c..7e6b26f5 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -188,23 +188,22 @@ globals story_impl::new_globals_from_snapshot(const snapshot& data) if (! snapshot.can_be_migrated(*this)) { return globals(); } - auto* globs = new globals_impl(this); + globals globs = new_globals(); snapshot.strings().clear(); snapshot_interface::loader loader(snapshot.strings(), _string_table, snapshot.can_be_migrated()); - auto end = globs->snap_load(snapshot.get_globals_snap(), loader); + auto end = globs.cast()->snap_load(snapshot.get_globals_snap(), loader); inkAssert(end == snapshot.get_runner_snap(0), "not all data were used for global reconstruction"); if (hash() != snapshot.hash()) { globals new_globs = new_globals(); runner thread = new_runner(new_globs); - if (! globs->migrate_new_globals( + if (! globs.cast()->migrate_new_globals( *new_globs.cast().get(), reinterpret_cast(snapshot.get_list_metadata()) )) { - delete globs; return globals(); } } - return globals(globs, _block); + return globs; } runner story_impl::new_runner(globals store) @@ -294,7 +293,7 @@ void story_impl::setup_pointers() ( uint32_t ) (end() - _file), ( uint32_t ) (_instruction_data - _file) + header._instructions._bytes ); - _length = _instruction_data + header._instructions._bytes - _file; + _length = static_cast(_instruction_data + header._instructions._bytes - _file); // Debugging info /*{ diff --git a/inkcpp_c/include/inkcpp.h b/inkcpp_c/include/inkcpp.h index 39513821..d8de9579 100644 --- a/inkcpp_c/include/inkcpp.h +++ b/inkcpp_c/include/inkcpp.h @@ -184,6 +184,13 @@ typedef struct HInkSTory HInkStory; */ int ink_list_iter_next(InkListIter* self); +#ifdef __GNUC__ +#else +# pragma warning(push) +// we use a anonymus union for convinence, feel free to change this in the future if problems should +// occure. +# pragma warning(disable : 4201) +#endif /** Repserentation of a ink variable. * @ingroup clib * The concret type contained is noted in @ref InkValue::type "type", please use this information @@ -217,6 +224,10 @@ typedef struct HInkSTory HInkStory; ValueTypeList ///< a ink list } type; ///< indicates type contained in value }; +#ifdef __GNUC__ +#else +# pragma warning(pop) +#endif // const char* ink_value_to_string(const InkValue* self); diff --git a/inkcpp_c/inkcpp.cpp b/inkcpp_c/inkcpp.cpp index b7008e2c..2a33b6e0 100644 --- a/inkcpp_c/inkcpp.cpp +++ b/inkcpp_c/inkcpp.cpp @@ -84,7 +84,7 @@ extern "C" { fseek(file, 0, SEEK_SET); unsigned char* data = static_cast(malloc(file_length)); inkAssert(data, "Malloc of size %u failed", file_length); - unsigned length = fread(data, sizeof(unsigned char), static_cast(file_length), file); + unsigned length = static_cast(fread(data, sizeof(unsigned char), static_cast(file_length), file)); inkAssert( file_length == static_cast(length), "Expected to read file of size %u, but only read %u", file_length, length diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index 594c3600..a5119dcb 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -246,7 +246,7 @@ void binary_emitter::output(std::ostream& out) // Create container data std::vector container_data; container_data.resize(_max_container_index); - build_container_data(container_data, ~0, _root); + build_container_data(container_data, ~0U, _root); // Create container hash (and write the hashes into the data as well) std::vector container_hash; @@ -275,9 +275,9 @@ void binary_emitter::output(std::ostream& out) header._strings.setup(offset, _strings.pos()); header._list_meta.setup(offset, _list_meta.pos()); header._lists.setup(offset, _lists.pos()); - header._containers.setup(offset, container_data.size() * sizeof(container_data_t)); - header._container_map.setup(offset, _container_map.size() * sizeof(container_map_t)); - header._container_hash.setup(offset, container_hash.size() * sizeof(container_hash_t)); + header._containers.setup(offset, static_cast(container_data.size() * sizeof(container_data_t))); + header._container_map.setup(offset, static_cast(_container_map.size() * sizeof(container_map_t))); + header._container_hash.setup(offset, static_cast(container_hash.size() * sizeof(container_hash_t))); header._instructions.setup(offset, _instructions.pos()); // Write the header diff --git a/inkcpp_compiler/emitter.cpp b/inkcpp_compiler/emitter.cpp index 39e27fd3..a6538d52 100644 --- a/inkcpp_compiler/emitter.cpp +++ b/inkcpp_compiler/emitter.cpp @@ -8,7 +8,7 @@ namespace ink::compiler::internal { -void emitter::start(int ink_version, compilation_results* results) +void emitter::start(uint16_t ink_version, compilation_results* results) { // store _ink_version = ink_version; diff --git a/inkcpp_compiler/emitter.h b/inkcpp_compiler/emitter.h index fa3eec21..8c18490d 100644 --- a/inkcpp_compiler/emitter.h +++ b/inkcpp_compiler/emitter.h @@ -24,7 +24,7 @@ class emitter : public reporter virtual ~emitter() {} // starts up the emitter (and calls initialize) - void start(int ink_version, compilation_results* results = nullptr); + void start(uint16_t ink_version, compilation_results* results = nullptr); // tells the emitter compilation is done (and calls finalize) void finish(container_t max_container_index); @@ -107,6 +107,6 @@ class emitter : public reporter container_t _max_container_index; // ink version - int _ink_version; + uint16_t _ink_version; }; } // namespace ink::compiler::internal diff --git a/inkcpp_compiler/json_compiler.h b/inkcpp_compiler/json_compiler.h index 39c2fda7..52a3db91 100644 --- a/inkcpp_compiler/json_compiler.h +++ b/inkcpp_compiler/json_compiler.h @@ -61,6 +61,6 @@ class json_compiler : public reporter container_t _next_container_index; list_data _list_meta; - int _ink_version; + uint16_t _ink_version; }; } // namespace ink::compiler::internal diff --git a/inkcpp_test/CMakeLists.txt b/inkcpp_test/CMakeLists.txt index d8c01499..95b757ef 100644 --- a/inkcpp_test/CMakeLists.txt +++ b/inkcpp_test/CMakeLists.txt @@ -30,7 +30,9 @@ add_executable( MoveTo.cpp ListMatching.cpp Fixes.cpp - Migration.cpp) + Migration.cpp + MultiRunner.cpp +) target_link_libraries(inkcpp_test PUBLIC inkcpp inkcpp_compiler inkcpp_shared) target_include_directories(inkcpp_test PRIVATE ../shared/private/) diff --git a/shared/public/system.h b/shared/public/system.h index d2ae0b12..b3542147 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -149,10 +149,8 @@ namespace internal #ifdef __GNUC__ #else # pragma warning(push) -# pragma warning( \ - disable : 4514, \ - justification : "functions are defined in header file, they do not need to be used." \ - ) +// functions are defined in header file, they do not need to be used. +# pragma warning(disable : 4514) #endif /** Checks if a string starts with a given prefix*/ static inline constexpr bool starts_with(const char* string, const char* prefix) @@ -237,10 +235,8 @@ class ink_exception # pragma GCC diagnostic ignored "-Wunused-parameter" #else # pragma warning(push) -# pragma warning( \ - disable : 4100, \ - justification : "dependend on rtti, exception and stl support not all arguments are needed" \ - ) +// dependend on rtti, exception and stl support not all arguments are needed +# pragma warning(disable : 4100) #endif // assert template diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index 4c9f080e..d2be5472 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -37,7 +37,12 @@ AInkRuntime::AInkRuntime() PrimaryActorTick.bCanEverTick = true; } -AInkRuntime::~AInkRuntime() {} +AInkRuntime::~AInkRuntime() { + if (mStableSnapshot.IsValid()) { + mStableSnapshot->SetValue(FInkSnapshot()); + mStableSnapshot.Reset(); + } +} // Called when the game starts or when spawned void AInkRuntime::BeginPlay() diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h index fff1eae8..c9512047 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h @@ -233,5 +233,6 @@ class INKCPP_API AInkRuntime : public AActor * the lambda checks it before firing and skips. Tokens are cleaned up lazily. */ TArray> mObserverTokens; TSharedPtr> mStableSnapshot; + UPROPERTY() TArray mYieldedThreadsForSnapshot; }; diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h b/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h index 6da7bf0a..681e5a6a 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h @@ -18,7 +18,7 @@ USTRUCT(BlueprintType) struct INKCPP_API FInkSnapshot { GENERATED_BODY() - FInkSnapshot() {} + FInkSnapshot() : Migratable(false) {} /** @private */ FInkSnapshot(const char* snap_data, size_t snap_len, bool migratable) @@ -43,7 +43,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( const FInkSnapshot&, Snapshot ); -UCLASS() +UCLASS(BlueprintType) class INKCPP_API UInkMigratableSnapshotAsync : public UBlueprintAsyncActionBase { GENERATED_BODY() @@ -54,7 +54,7 @@ class INKCPP_API UInkMigratableSnapshotAsync : public UBlueprintAsyncActionBase FInkMigratableSnapshotCompleted Completed; UFUNCTION(BlueprintCallable, - meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject")) + meta = (BlueprintInternalUseOnly = "true")) static UInkMigratableSnapshotAsync* GetMigratableSnapshot( AInkRuntime* Runtime); From 16dfa077e91a7682c627f79524bab22314017fd2 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Sun, 31 May 2026 21:53:49 +0200 Subject: [PATCH 08/29] fix(Migration): list_table migration had a of by one error --- inkcpp/list_table.cpp | 3 +- inkcpp/list_table.h | 5 +- inkcpp_test/MultiRunner.cpp | 96 ++++++++++++++++++++++++ inkcpp_test/ink/UE_example.ink | 105 ++++++++++++++++++++++++++ inkcpp_test/ink/UE_example_v2.ink | 121 ++++++++++++++++++++++++++++++ 5 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 inkcpp_test/MultiRunner.cpp create mode 100644 inkcpp_test/ink/UE_example.ink create mode 100644 inkcpp_test/ink/UE_example_v2.ink diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index e32dea84..8fd06d04 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -1092,11 +1092,12 @@ bool list_table::migrate(const char* old_list_metadata) is_empty_list = false; for (size_t j = old_ref_table.listBegin(i); j < old_ref_table._list_end[i]; ++j) { if (old_ref_table.hasFlag(entry, j) && old_ref_table._flag_names[j]) { + migrated = false; if (value_matches[j] != -1) { hit = true; migrated = true; size_t k; - for (k = 0; _list_end[k] < static_cast(value_matches[j]); ++k) {} + for (k = 0; _list_end[k] <= static_cast(value_matches[j]); ++k) {} setList(new_entry, k); setFlag(new_entry, value_matches[j]); } diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h index a30a82ab..c55c77d8 100644 --- a/inkcpp/list_table.h +++ b/inkcpp/list_table.h @@ -148,7 +148,10 @@ class list_table : public snapshot_interface size_t snap(unsigned char* data, const snapper&) const; const unsigned char* snap_load(const unsigned char* data, const loader&); - bool can_be_migrated() const { return _list_handouts.size() == 0; } + bool can_be_migrated() const + { + return true; + } /** special treatment when a list gets assigned again * when a list gets assigned and would have no origin, it gets the origin of the base with origin diff --git a/inkcpp_test/MultiRunner.cpp b/inkcpp_test/MultiRunner.cpp new file mode 100644 index 00000000..4715b5c7 --- /dev/null +++ b/inkcpp_test/MultiRunner.cpp @@ -0,0 +1,96 @@ +#include "catch.hpp" +#include "../snapshot_impl.h" +#include "list.h" +#include "system.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace ink::runtime; + +SCENARIO("UE example story with multiple runner") +{ + std::unique_ptr base_story{story::from_file(INK_TEST_RESOURCE_DIR "UE_example.bin")}; + std::unique_ptr story_v2{story::from_file(INK_TEST_RESOURCE_DIR "UE_example_v2.bin")}; + globals base_globals = base_story->new_globals(); + runner side_thread = base_story->new_runner(base_globals); + GIVEN("a not starde story should be migratable") + { + std::unique_ptr snap{base_globals->create_snapshot()}; + REQUIRE(snap->can_be_migrated()); + } + REQUIRE(side_thread->move_to("Wait")); + REQUIRE(side_thread->getall() == ""); + GIVEN("a snapshot of a done runner should be migratable") + { + std::unique_ptr snap{base_globals->create_snapshot()}; + REQUIRE(snap->can_be_migrated()); + } + runner main_thread = base_story->new_runner(base_globals); + REQUIRE( + main_thread->getall() + == "You step outside your car. Its a wired feeling beehing here again.\n" + ); + GIVEN("two runner one terminate one in choice should not be migratble") + { + std::unique_ptr snap{base_globals->create_snapshot()}; + REQUIRE_FALSE(snap->can_be_migrated()); + } + GIVEN("two runner, one done the other behind choice should be migratable") + { + main_thread->choose(0); + std::unique_ptr snap{base_globals->create_snapshot()}; + REQUIRE(snap->can_be_migrated()); + } + GIVEN("Migration") + { + main_thread->choose(1); + main_thread->getall(); + main_thread->choose(1); + main_thread->getall(); + main_thread->choose(1); + THEN("Inventory should be as expected") + { + ink::optional inventory = base_globals->get("Inventory"); + REQUIRE(inventory); + list inventory_list = inventory.value().get(); + list_interface::iterator list_iter = inventory_list->begin(); + REQUIRE(list_iter != inventory_list->end()); + list_interface::iterator::Flag flag = *list_iter; + REQUIRE(flag.flag_name == std::string("Skull")); + REQUIRE(flag.list_name == std::string("Clues")); + ++list_iter; + REQUIRE(list_iter != inventory_list->end()); + flag = *list_iter; + REQUIRE(flag.flag_name == std::string("TalkWithAnimals")); + REQUIRE(flag.list_name == std::string("Potions")); + } + + std::unique_ptr snap{base_globals->create_snapshot()}; + globals globals_v2 = story_v2->new_globals_from_snapshot(*snap); + runner main_thread_v2 = story_v2->new_runner_from_snapshot(*snap, globals_v2, 1); + + THEN("Inventory should be still the same") + { + REQUIRE(main_thread_v2->getall() == "\"Ahh\", you cry while reaching for the door bell. Saying it was charched would be an understatement.\n"); + ink::optional inventory = globals_v2->get("Inventory"); + REQUIRE(inventory); + list inventory_list = inventory.value().get(); + list_interface::iterator list_iter = inventory_list->begin(); + REQUIRE(list_iter != inventory_list->end()); + list_interface::iterator::Flag flag = *list_iter; + REQUIRE(flag.flag_name == std::string("Skull")); + REQUIRE(flag.list_name == std::string("Clues")); + ++list_iter; + REQUIRE(list_iter != inventory_list->end()); + flag = *list_iter; + REQUIRE(flag.flag_name == std::string("TalkWithAnimals")); + REQUIRE(flag.list_name == std::string("Potions")); + } + } +} diff --git a/inkcpp_test/ink/UE_example.ink b/inkcpp_test/ink/UE_example.ink new file mode 100644 index 00000000..ab276a5a --- /dev/null +++ b/inkcpp_test/ink/UE_example.ink @@ -0,0 +1,105 @@ +# date:2025.03.22 +LIST Potions = TalkWithAnimals, Invisibility +LIST Clues = Skull, Feather +LIST Knowladge = YellowDress +VAR Inventory = (Skull, TalkWithAnimals) +VAR Health = 100 +LIST StatusConditions = CanTalkWithAniamls, IsInvisible + +-> Mansion.Car + +EXTERNAL transition(to) +=== function transition(to) === +~ return 0 + +=== function walking(to) === +You startk walking to {to}. +~ transition(to) +~ return + +=== Wait +-> DONE + +=== TClues += TSkull + A human skull, why do I have this again? + -> END += TFeather + A Large Feather found insede the dining room, I wounder from which bird it is. + -> END +=== TPotions += TTalkWithAnimals + A potion which allows the consumer to talk with a variaty of animals. Just make sure + your serroundings do not think you are crazy. + {StatusConditions ? (CanTalkWithAniamls): Drinking more of it will not increase the effect.} + + [Put it Back] + You put the potion back into your pouch. + -> END + + {not (StatusConditions ? CanTalkWithAniamls)} [Drink] + ~ StatusConditions += CanTalkWithAniamls + A take a sip. The potion tastes like Hores, it is afull. + -> END += TInvisibility + A potion which allows the consumer to stay unseen for the human eye. Not tested + wtih other speccies. + {StatusConditions ? (IsInvisible): Drinking more of it will not increase the effect.} + + [Put it Back] + You put the potion back into your pouch. + -> END + + {not (StatusConditions ? (IsInvisible))}[Drink] + ~ StatusConditions += IsInvisible + You put a small drop on your thoungh. It feels like liking a battery, such a nice feeling + -> END + +=== Faint +# background:Unconscious +You collapse, the next thing you can remember is how you are given to the ambulance. +No further action for today ... +-> DONE + +=== Mansion += Car +# background:Car +You step outside your car. Its a wired feeling beehing here again. +-> Car_cycle += Car_cycle +# background:Car ++ (look_around)[look around # Type:Idle] + It is a strange day. Despite it beeing spring, the sky is one massive gray soup. # style:Gray + -> Car_cycle ++ [go to the mension] + ~ walking("Mansion.Entrance") + -> Entrance + += Entrance +# background:Mansion +{not Mansion.look_around: + ~ Knowladge += YellowDress + Just in time you are able to see the door, someone with with a yellow summer dress enters it. +} +You're climbing the 56 steps up to the door; high tides are an annoying thing. +-> Entrance_cycle += Entrance_cycle +# background:Mansion ++ (look_around) [look around # Type:Idle ] + While watching around you, <> + {Inventory hasnt Invisibility: + see a small bottle in the Pot next to the door. + -else: + see nothing of intrest + } + -> Entrance_cycle +* {look_around} [Pick up the bottle] + ~ Inventory += Invisibility + You pick up the bottle and inspect it more. It is labeld "Invisible, just this one word written with and edding. + -> Entrance_cycle ++ (knock)[Knock {knock: again?} # {knock: Type:Danger} ] + ~ Health -= 20 + "Ahh", you cry while reaching for the door bell. Saying it was charched would be an understatement. + { Health <= 0: -> Faint} + -> Entrance_cycle ++ {knock && Knowladge ? (YellowDress)} [Inspect the Door] + You just saw someone enter, how did they do not get shoked? + It seems the developr run out of time, maybe in the next version we can continue? + -> Entrance_cycle +-> DONE \ No newline at end of file diff --git a/inkcpp_test/ink/UE_example_v2.ink b/inkcpp_test/ink/UE_example_v2.ink new file mode 100644 index 00000000..96b4563b --- /dev/null +++ b/inkcpp_test/ink/UE_example_v2.ink @@ -0,0 +1,121 @@ +# date:2025.03.22 +LIST Potions = TalkWithAnimals, Invisibility +LIST Clues = Skull, Feather +LIST Knowladge = YellowDress +VAR Inventory = (Skull, TalkWithAnimals) +VAR Health = 100 +LIST StatusConditions = CanTalkWithAniamls, IsInvisible + +-> Mansion.Car + +EXTERNAL transition(to) +=== function transition(to) === +~ return 0 + +=== function walking(to) === +You startk walking to {to}. +~ transition(to) +~ return + +=== Wait +-> DONE + +=== TClues += TSkull + A human skull, why do I have this again? + -> END += TFeather + A Large Feather found insede the dining room, I wounder from which bird it is. + -> END +=== TPotions += TTalkWithAnimals + A potion which allows the consumer to talk with a variaty of animals. Just make sure + your serroundings do not think you are crazy. + {StatusConditions ? (CanTalkWithAniamls): Drinking more of it will not increase the effect.} + + [Put it Back] + You put the potion back into your pouch. + -> END + + {not (StatusConditions ? CanTalkWithAniamls)} [Drink] + ~ StatusConditions += CanTalkWithAniamls + A take a sip. The potion tastes like Hores, it is afull. + -> END += TInvisibility + A potion which allows the consumer to stay unseen for the human eye. Not tested + wtih other speccies. + {StatusConditions ? (IsInvisible): Drinking more of it will not increase the effect.} + + [Put it Back] + You put the potion back into your pouch. + -> END + + {not (StatusConditions ? (IsInvisible))}[Drink] + ~ StatusConditions += IsInvisible + You put a small drop on your thoungh. It feels like liking a battery, such a nice feeling + -> END + +=== Faint +# background:Unconscious +You collapse, the next thing you can remember is how you are given to the ambulance. +No further action for today ... +-> DONE + +VAR MansionFrontDoorLocked = true +=== Mansion += Car +# background:Car +At first there is anger about the traffic jam and your 1h delay. But your memories of this place let this feeling fade away. +-> Car_cycle += Car_cycle +# background:Car ++ (look_around)[look around # Type:Idle] + It is a strange day. Despite it beeing spring, the sky is one massive gray soup. # style:Gray + -> Car_cycle ++ [go to the mension] + ~ walking("Mansion.Entrance") + -> Entrance + += Entrance +# background:Mansion +You're climbing the 56 steps up to the door; high tides are an annoying thing. +-> Entrance_cycle += Entrance_cycle +# background:Mansion ++ (look_around) [look around # Type:Idle ] + While watching around you, <> + {Inventory hasnt Invisibility: + see a small bottle in the Pot next to the door. + -else: + see nothing of intrest + } + -> Entrance_cycle +* {look_around} [Pick up the bottle] + ~ Inventory += Invisibility + You pick up the bottle and inspect it more. It is labeld "Invisible, just this one word written with and edding. + -> Entrance_cycle ++ (knock)[Knock {knock: again?} # {knock: Type:Danger} ] + ~ Health -= 20 + "Ahh", you cry while reaching for the door bell. Saying it was charched would be an understatement. + { Health <= 0: -> Faint} + -> Entrance_cycle ++ {knock && Knowladge ? (YellowDress)} [Inspect the Door] + You just saw someone enter, how did they do not get shoked? + -> Entrance_mouse -> + -> Entrance_cycle ++ {not MansionFrontDoorLocked} [Open the Door] + On high alert you press the door handle, it opens. Quite smooth and easy. + You step inside. + TODO: to be continue + -> DONE + += Entrance_mouse + Something hushes through a hole beside the door, after you come closer you see it. A little gray mouse, it looks quite eloquent. + + [Ask the mouse for help] + You try to formulate your dilemma and your annoyance about the doorbell. + {StatusConditions ? (CanTalkWithAniamls): + ~ MansionFrontDoorLocked = false + TODO: enter nice conversasion with a picky but helpful mouse + -else: + The mouse squeaked, then was silent. It seems it tried to answer you, but you are unable to understand it. + } + + A ugly little creature, ignore it. + - ->-> + +-> DONE \ No newline at end of file From 25834ee78330996ff932a193bc4db0f52a259c13 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 1 Jun 2026 20:41:34 +0200 Subject: [PATCH 09/29] fix(List/Migration): list equivalence was calculated wrong --- inkcpp/list_table.cpp | 2 +- inkcpp/story_impl.cpp | 12 +++-- inkcpp_test/ListMatching.cpp | 10 ++-- inkcpp_test/MultiRunner.cpp | 46 ++++++++++++++++++- inkcpp_test/ink/UE_example_v2.ink | 6 +-- .../Source/inkcpp/Private/InkSnapshot.cpp | 41 +++++++++++++++++ 6 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 unreal/inkcpp/Source/inkcpp/Private/InkSnapshot.cpp diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index 8fd06d04..eaff0cbc 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -905,7 +905,7 @@ float d_contains(const size_t lh[2], const size_t rh[2], const int* matches) } } n_union -= n_intersection; - return static_cast(n_intersection) / n_union; + return 1.f - static_cast(n_intersection) / n_union; } /** Distance function for string labels. diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index 7e6b26f5..1084fa95 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -218,7 +218,7 @@ runner story_impl::new_runner_from_snapshot(const snapshot& data, globals store, const snapshot_impl& snapshot = reinterpret_cast(data); if (store == nullptr) store = new_globals_from_snapshot(snapshot); - auto* run = new runner_impl(this, store); + runner run(new runner_impl(this, store), _block); // snapshot id is inverso of creation time, but creation time is the more intouitve numbering to // use idx = (data.num_runners() - idx - 1); @@ -227,7 +227,7 @@ runner story_impl::new_runner_from_snapshot(const snapshot& data, globals store, _string_table, snapshot.can_be_migrated(), }; - auto end = run->snap_load(snapshot.get_runner_snap(idx), loader); + auto end = run.cast()->snap_load(snapshot.get_runner_snap(idx), loader); inkAssert( (idx + 1 < snapshot.num_runners() && end == snapshot.get_runner_snap(idx + 1)) || end == snapshot.get_data() + snapshot.get_data_len() @@ -235,11 +235,15 @@ runner story_impl::new_runner_from_snapshot(const snapshot& data, globals store, "not all data were used for runner reconstruction" ); if (hash() != snapshot.hash()) { - if (! run->migrate_to(*reinterpret_cast(snapshot.get_runner_snap(idx)))) { + hash_t current_node = *reinterpret_cast(snapshot.get_runner_snap(idx)); + if (current_node == 0) { + return run; + } + if (! run.cast()->migrate_to(current_node)) { return runner(); } } - return runner(run, _block); + return run; } void story_impl::setup_pointers() diff --git a/inkcpp_test/ListMatching.cpp b/inkcpp_test/ListMatching.cpp index 830b43de..d29eb6ef 100644 --- a/inkcpp_test/ListMatching.cpp +++ b/inkcpp_test/ListMatching.cpp @@ -88,7 +88,7 @@ SCENARIO("santy check distance functions", "[list_match]") ink::size_t rh[] = {5, 10}; int matches[] = {0, 0, 0, 0, 0, 5, 6, 7, 8, 9}; float d = ink::runtime::internal::d_contains(lh, rh, matches); - CHECK_THAT(d, Catch::Matchers::WithinAbs(1, 0.001)); + CHECK_THAT(d, Catch::Matchers::WithinAbs(0, 0.001)); } GIVEN("Dropped Values") { @@ -96,7 +96,7 @@ SCENARIO("santy check distance functions", "[list_match]") ink::size_t rh[] = {5, 8}; int matches[] = {0, 0, 0, 0, 0, 5, -1, -1, 6, 7}; float d = ink::runtime::internal::d_contains(lh, rh, matches); - CHECK_THAT(d, Catch::Matchers::WithinAbs(0.6, 0.001)); + CHECK_THAT(d, Catch::Matchers::WithinAbs(0.4, 0.001)); } GIVEN("New Values") { @@ -104,7 +104,7 @@ SCENARIO("santy check distance functions", "[list_match]") ink::size_t rh[] = {5, 10}; int matches[] = {0, 0, 0, 0, 0, 5, 6, 7}; float d = ink::runtime::internal::d_contains(lh, rh, matches); - CHECK_THAT(d, Catch::Matchers::WithinAbs(0.6, 0.001)); + CHECK_THAT(d, Catch::Matchers::WithinAbs(0.4, 0.001)); } GIVEN("Swapped Values") { @@ -112,7 +112,7 @@ SCENARIO("santy check distance functions", "[list_match]") ink::size_t rh[] = {5, 10}; int matches[] = {0, 0, 0, 0, 0, 5, 9, 6, 8, 7}; float d = ink::runtime::internal::d_contains(lh, rh, matches); - CHECK_THAT(d, Catch::Matchers::WithinAbs(1, 0.001)); + CHECK_THAT(d, Catch::Matchers::WithinAbs(0, 0.001)); } GIVEN("Changed Values") { @@ -120,7 +120,7 @@ SCENARIO("santy check distance functions", "[list_match]") ink::size_t rh[] = {5, 10}; int matches[] = {0, 0, 0, 0, 0, 5, 9, -1, -1, -1}; float d = ink::runtime::internal::d_contains(lh, rh, matches); - CHECK_THAT(d, Catch::Matchers::WithinAbs(0.25, 0.001)); + CHECK_THAT(d, Catch::Matchers::WithinAbs(0.75, 0.001)); } } } diff --git a/inkcpp_test/MultiRunner.cpp b/inkcpp_test/MultiRunner.cpp index 4715b5c7..99b48962 100644 --- a/inkcpp_test/MultiRunner.cpp +++ b/inkcpp_test/MultiRunner.cpp @@ -10,6 +10,7 @@ #include #include #include +#include using namespace ink::runtime; @@ -74,10 +75,10 @@ SCENARIO("UE example story with multiple runner") std::unique_ptr snap{base_globals->create_snapshot()}; globals globals_v2 = story_v2->new_globals_from_snapshot(*snap); runner main_thread_v2 = story_v2->new_runner_from_snapshot(*snap, globals_v2, 1); + REQUIRE(main_thread_v2->getall() == "\"Ahh\", you cry while reaching for the door bell. Saying it was charched would be an understatement.\n"); THEN("Inventory should be still the same") { - REQUIRE(main_thread_v2->getall() == "\"Ahh\", you cry while reaching for the door bell. Saying it was charched would be an understatement.\n"); ink::optional inventory = globals_v2->get("Inventory"); REQUIRE(inventory); list inventory_list = inventory.value().get(); @@ -92,5 +93,48 @@ SCENARIO("UE example story with multiple runner") REQUIRE(flag.flag_name == std::string("TalkWithAnimals")); REQUIRE(flag.list_name == std::string("Potions")); } + + runner side_thread_v2 = story_v2->new_runner_from_snapshot(*snap, globals_v2, 0); + REQUIRE(side_thread_v2->move_to("TPotions.TTalkWithAnimals")); + REQUIRE(side_thread_v2->getall() == "A potion which allows the consumer to talk with a variaty of animals. Just make sure\nyour serroundings do not think you are crazy.\n"); + side_thread_v2->choose(1); + REQUIRE( + side_thread_v2->getall() == "A take a sip. The potion tastes like Hores, it is afull.\n" + ); + REQUIRE_FALSE(side_thread_v2->can_continue()); + REQUIRE_FALSE(side_thread_v2->has_choices()); + + THEN("We should now can talk with Animals") + { + { + ink::optional state = globals_v2->get("StatusConditions"); + REQUIRE(state); + list state_list = state.value().get(); + list_interface::iterator list_iter = state_list->begin(); + REQUIRE(list_iter != state_list->end()); + list_interface::iterator::Flag flag = *list_iter; + REQUIRE(flag.flag_name == std::string("CanTalkWithAniamls")); + REQUIRE(flag.list_name == std::string("StatusConditions")); + ++list_iter; + REQUIRE(list_iter == state_list->end()); + } + { + ink::optional prototype = globals_v2->get("CanTalkWithAniamls"); + REQUIRE(prototype); + list prototype_flag = prototype.value().get(); + list_interface::iterator list_iter = prototype_flag->begin(); + REQUIRE(list_iter != prototype_flag->end()); + list_interface::iterator::Flag flag = *list_iter; + REQUIRE(flag.flag_name == std::string("CanTalkWithAniamls")); + REQUIRE(flag.list_name == std::string("StatusConditions")); + ++list_iter; + REQUIRE(list_iter == prototype_flag->end()); + } + } + + main_thread_v2->choose(2); + REQUIRE(main_thread_v2->getall() == "You just saw someone enter, how did they do not get shoked?\nSomething hushes through a hole beside the door, after you come closer you see it. A little gray mouse, it looks quite eloquent.\n"); + main_thread_v2->choose(0); + REQUIRE(main_thread_v2->getall() == "You try to formulate your dilemma and your annoyance about the doorbell.\n(enter nice conversasion with a picky but helpful mouse)\n"); } } diff --git a/inkcpp_test/ink/UE_example_v2.ink b/inkcpp_test/ink/UE_example_v2.ink index 96b4563b..56c3c4dc 100644 --- a/inkcpp_test/ink/UE_example_v2.ink +++ b/inkcpp_test/ink/UE_example_v2.ink @@ -95,14 +95,14 @@ You're climbing the 56 steps up to the door; high tides are an annoying thing. "Ahh", you cry while reaching for the door bell. Saying it was charched would be an understatement. { Health <= 0: -> Faint} -> Entrance_cycle -+ {knock && Knowladge ? (YellowDress)} [Inspect the Door] ++ {knock && Knowladge ? YellowDress} [Inspect the Door] You just saw someone enter, how did they do not get shoked? -> Entrance_mouse -> -> Entrance_cycle + {not MansionFrontDoorLocked} [Open the Door] On high alert you press the door handle, it opens. Quite smooth and easy. You step inside. - TODO: to be continue + ToBeContinued -> DONE = Entrance_mouse @@ -111,7 +111,7 @@ You're climbing the 56 steps up to the door; high tides are an annoying thing. You try to formulate your dilemma and your annoyance about the doorbell. {StatusConditions ? (CanTalkWithAniamls): ~ MansionFrontDoorLocked = false - TODO: enter nice conversasion with a picky but helpful mouse + (enter nice conversasion with a picky but helpful mouse) -else: The mouse squeaked, then was silent. It seems it tried to answer you, but you are unable to understand it. } diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkSnapshot.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkSnapshot.cpp new file mode 100644 index 00000000..80ba547b --- /dev/null +++ b/unreal/inkcpp/Source/inkcpp/Private/InkSnapshot.cpp @@ -0,0 +1,41 @@ +#include "InkSnapshot.h" +#include "InkRuntime.h" + +#include "Async/Async.h" + +UInkMigratableSnapshotAsync* + UInkMigratableSnapshotAsync::GetMigratableSnapshot(AInkRuntime* Runtime) +{ + UInkMigratableSnapshotAsync* Node = NewObject(); + Node->Runtime = Runtime; + return Node; +} + +void UInkMigratableSnapshotAsync::Activate() +{ + if (! Runtime) { + Completed.Broadcast(FInkSnapshot()); + SetReadyToDestroy(); + return; + } + + TFuture Future = Runtime->MigratableSnapshot(); + + TWeakObjectPtr WeakThis(this); + + Future.Next([WeakThis](FInkSnapshot Snapshot) { + AsyncTask(ENamedThreads::GameThread, [WeakThis, Snapshot]() { + if (! WeakThis.IsValid()) + return; + + WeakThis->Completed.Broadcast(Snapshot); + WeakThis->SetReadyToDestroy(); + }); + }); +} + +void UInkMigratableSnapshotAsync::HandleResult(const FInkSnapshot& Snapshot) +{ + Completed.Broadcast(Snapshot); + SetReadyToDestroy(); +} From 1402ce2a9193f1152ca3e990e64849ef14b17efb Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 3 Jun 2026 16:48:04 +0200 Subject: [PATCH 10/29] StartImplementing ListMigration correctly --- inkcpp/globals_impl.cpp | 33 +++++------------------------- inkcpp/globals_impl.h | 2 -- inkcpp/list_table.cpp | 45 ++++++++++++++++++++++++++++++++++++----- inkcpp/list_table.h | 14 ++++++++----- inkcpp_python/pybind11 | 2 +- 5 files changed, 55 insertions(+), 41 deletions(-) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 90bc36e0..0d99be5b 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -26,27 +26,7 @@ globals_impl::globals_impl(const story_impl* story) _visit_counts.resize(_num_containers); if (_lists) { // initialize static lists - init_static_list_flags(); - } -} - -void globals_impl::init_static_list_flags() -{ - const list_flag* flags = _owner->lists(); - while (*flags != null_flag) { - list_table::list l = _lists.create_permament(); - while (*flags != null_flag) { - list_flag flag = _lists.external_fvalue_to_internal(*flags); - _lists.add_inplace(l, flag); - ++flags; - } - ++flags; - } - for (const auto& flag : _lists.named_flags()) { - set_variable( - hash_string(flag.name), - value{}.set(list_flag{flag.flag.list_id, flag.flag.flag}) - ); + _lists.init_static_list_flags(_owner->lists(), _variables); } } @@ -302,8 +282,8 @@ const unsigned char* globals_impl::snap_load(const unsigned char* ptr, const loa ); for (size_t i = 0; i < old_capacity; ++i) { hash_t path; - ptr = snap_read(ptr, path); - container_t c_id = ~0U; + ptr = snap_read(ptr, path); + container_t c_id = ~0U; ip_t container_ip = _owner->find_offset_for(path); bool found = container_ip != nullptr && _owner->find_container_id( @@ -334,14 +314,11 @@ const unsigned char* globals_impl::snap_load(const unsigned char* ptr, const loa bool globals_impl::migrate_new_globals(globals_impl& new_globals, const char* list_metadata) { - bool success - = _variables.migrate(new_globals._variables) && ((! _lists) || _lists.migrate(list_metadata)); + bool success = _variables.migrate(new_globals._variables) + && ((! _lists) || _lists.migrate(list_metadata, _owner->lists(), _variables)); if (! success) { return false; } - if (_lists) { - init_static_list_flags(); - } return true; } diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index d4dc1fe6..5c11807a 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -30,8 +30,6 @@ class globals_impl final { friend snapshot_impl; - void init_static_list_flags(); - public: size_t snap(unsigned char* data, const snapper&) const; const unsigned char* snap_load(const unsigned char* data, const loader&); diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index eaff0cbc..5d9ae998 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -13,6 +13,7 @@ #include "random.h" #include "string_utils.h" #include "list_impl.h" +#include "stack.h" #include #ifdef INK_ENABLE_STL @@ -1013,7 +1014,9 @@ float* cost_matrix(const MatchListValues& lh, const MatchListValues& rh, float d return matrix; } -bool list_table::migrate(const char* old_list_metadata) +bool list_table::migrate( + const char* old_list_metadata, const list_flag* permanent_lists, basic_stack& variables +) { list_table old_ref_table(old_list_metadata); for (const auto& x : _data) { @@ -1025,6 +1028,7 @@ bool list_table::migrate(const char* old_list_metadata) _data.clear(); _entry_state.clear(); + impl_init_static_list(permanent_lists); // find best mapping between old and new list elements // + c_ij(value) = min(|v_i - v_j|/Rv,1) // + c_ij(name) = levenshtein, cosine n-grams, jaro-winkler @@ -1069,14 +1073,19 @@ bool list_table::migrate(const char* old_list_metadata) // low confidence list_value matches algorithms::hungarian_solver(value_matrix, value_matches, n_flags, LOW_CONFIDANCE_DROP_PANELTY); - for (size_t idx = 0; idx < old_ref_table._entry_state.size(); ++idx) { + + for (value& entry : variables.get_all()) { + list old_list = entry.get(); + size_t idx = old_list.lid; // migrate - list new_list{-1}; + list new_list{-1}; switch (old_ref_table._entry_state[idx]) { - case state::permanent: new_list = create_permament_at(idx); break; - case state::used: new_list = create_at(idx); break; + case state::permanent: new_list = create_permament(); break; + case state::used: new_list = create(); break; default: continue; } + entry.set(list(new_list)); + inkAssert(new_list.lid >= 0, "Failed to create new list entry for migration."); inkAssert( static_cast(new_list.lid) == idx, @@ -1128,5 +1137,31 @@ bool list_table::migrate(const char* old_list_metadata) return true; } +void list_table::impl_init_static_list(const list_flag* permanent_lists) +{ + const list_flag* flags = permanent_lists; + while (*flags != null_flag) { + list_table::list l = create_permament(); + while (*flags != null_flag) { + list_flag flag = external_fvalue_to_internal(*flags); + add_inplace(l, flag); + ++flags; + } + ++flags; + } +} + +void list_table::init_static_list_flags(const list_flag* permanent_lists, basic_stack& variables) +{ + impl_init_static_list(permanent_lists); + + for (const auto& flag : named_flags()) { + variables.set( + hash_string(flag.name), + value{}.set(list_flag{flag.flag.list_id, flag.flag.flag}) + ); + } +} + } // namespace ink::runtime::internal diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h index c55c77d8..bf05c3ec 100644 --- a/inkcpp/list_table.h +++ b/inkcpp/list_table.h @@ -22,6 +22,7 @@ struct header; namespace ink::runtime::internal { +class basic_stack; class prng; // TODO: move to utils @@ -114,8 +115,11 @@ class list_table : public snapshot_interface list& add_inplace(list& lh, list_flag rh); list_table(const char* data); + void init_static_list_flags(const list_flag* permanent_lists, basic_stack& variables); // binary list metadata of currently loaded list - bool migrate(const char* old_list_metadata); + bool migrate( + const char* old_list_metadata, const list_flag* permanent_lists, basic_stack& variables + ); explicit list_table() : _entrySize{0} @@ -148,10 +152,7 @@ class list_table : public snapshot_interface size_t snap(unsigned char* data, const snapper&) const; const unsigned char* snap_load(const unsigned char* data, const loader&); - bool can_be_migrated() const - { - return true; - } + bool can_be_migrated() const { return true; } /** special treatment when a list gets assigned again * when a list gets assigned and would have no origin, it gets the origin of the base with origin @@ -269,6 +270,9 @@ class list_table : public snapshot_interface list_interface* handout_list(list); private: + /** initelizes static lists defined in the story. */ + void impl_init_static_list(const list_flag* permanent_lists); + /** create a list with id == idx. * @attention used for migration only * @sa create() diff --git a/inkcpp_python/pybind11 b/inkcpp_python/pybind11 index 0c69e1eb..1b499083 160000 --- a/inkcpp_python/pybind11 +++ b/inkcpp_python/pybind11 @@ -1 +1 @@ -Subproject commit 0c69e1eb2177fa8f8580632c7b1f97fdb606ce8f +Subproject commit 1b4990838904501de7110d27e96c0a4152029156 From 30952c348d96bb3639affc8e2acd693b05a4d244 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 3 Jun 2026 22:38:15 +0200 Subject: [PATCH 11/29] fix(ListTable): init reused list memory --- inkcpp/list_table.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index 5d9ae998..262b5a93 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -72,6 +72,10 @@ list_table::list list_table::create() for (size_t i = 0; i < _entry_state.size(); ++i) { if (_entry_state[i] == state::empty) { _entry_state[i] = state::used; + memset( + _data.begin() + static_cast(_entrySize) * static_cast(i), 0, + _entrySize + ); return list(i); } } @@ -90,16 +94,24 @@ list_table::list list_table::create_at(size_t idx) if (idx < _entry_state.size()) { if (_entry_state[idx] == state::empty) { _entry_state[idx] = state::used; + memset( + _data.begin() + static_cast(_entrySize) * static_cast(idx), 0, + _entrySize + ); return list(idx); } return list(-1); } - while (_entry_state.size() <= idx) { + while (_entry_state.size() < idx) { _entry_state.push() = state::empty; for (int i = 0; i < _entrySize; ++i) { _data.push() = 0; } } + _entry_state.push() = state::used; + for (int i = 0; i < _entrySize; ++i) { + _data.push() = 0; + } return list(idx); } From 452d866fdef26c51647cd52ab684e36bb879b030 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 4 Jun 2026 00:48:22 +0200 Subject: [PATCH 12/29] FirstSctach: migrate list variables --- inkcpp/collections/restorable.h | 2 +- inkcpp/globals_impl.cpp | 24 ++++- inkcpp/globals_impl.h | 2 +- inkcpp/list_table.cpp | 175 ++++++++++++++++++-------------- inkcpp/list_table.h | 13 ++- inkcpp/runner_impl.cpp | 13 ++- inkcpp/runner_impl.h | 2 +- inkcpp/snapshot_impl.cpp | 10 ++ inkcpp/snapshot_impl.h | 17 ++-- inkcpp/snapshot_interface.h | 14 ++- inkcpp/stack.h | 1 + inkcpp/story_impl.cpp | 17 +++- inkcpp_python/pybind11 | 2 +- inkcpp_test/MultiRunner.cpp | 65 ++++++++---- 14 files changed, 239 insertions(+), 118 deletions(-) diff --git a/inkcpp/collections/restorable.h b/inkcpp/collections/restorable.h index 9dda466a..358578e5 100644 --- a/inkcpp/collections/restorable.h +++ b/inkcpp/collections/restorable.h @@ -228,7 +228,7 @@ class restorable : public snapshot_interface // Forward iterate template - void for_each(CallbackMethod callback, IsNullPredicate isNull) const + void for_each(CallbackMethod callback, IsNullPredicate isNull) { if (_pos == 0) { return; diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 0d99be5b..18f40121 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -312,13 +312,29 @@ const unsigned char* globals_impl::snap_load(const unsigned char* ptr, const loa return ptr; } -bool globals_impl::migrate_new_globals(globals_impl& new_globals, const char* list_metadata) +bool globals_impl::migrate_new_globals( + const loader& loader, globals_impl& new_globals, const char* list_metadata +) { - bool success = _variables.migrate(new_globals._variables) - && ((! _lists) || _lists.migrate(list_metadata, _owner->lists(), _variables)); - if (! success) { + if (! _variables.migrate(new_globals._variables)) { return false; } + if (_lists && ! loader.old_ref_table) { + if (! _lists.create_match_lut( + list_metadata, loader.list_list_matches, loader.list_value_matches, loader.old_ref_table + )) { + return false; + } + } + if (_lists) { + _lists.init_static_list_flags(_owner->lists(), _variables); + if (! _lists.migrate_variables( + loader.list_old_new_map, loader.list_list_matches, loader.list_value_matches, + *loader.old_ref_table, _variables + )) { + return false; + } + } return true; } diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index 5c11807a..caf9dd83 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -43,7 +43,7 @@ class globals_impl final * @param[in] list_metadata old list metadata to migrate list * the globals stored inside. */ - bool migrate_new_globals(globals_impl& new_globals, const char* list_metadata); + bool migrate_new_globals(const loader& loader, globals_impl& new_globals, const char* list_metadata); // Initializes a new global store from the given story globals_impl(const story_impl*); diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index 262b5a93..3d7de8ee 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -5,6 +5,7 @@ * https://github.com/JBenda/inkcpp for full license details. */ #include "list_table.h" +#include "array.h" #include "config.h" #include "hungarian_solver.h" #include "system.h" @@ -1026,21 +1027,24 @@ float* cost_matrix(const MatchListValues& lh, const MatchListValues& rh, float d return matrix; } -bool list_table::migrate( - const char* old_list_metadata, const list_flag* permanent_lists, basic_stack& variables +bool list_table::create_match_lut( + const char* old_list_metadata, + ink::runtime::internal::managed_array& list_list_matches, + ink::runtime::internal::managed_array& list_value_matches, + const list_table*& old_ref_table ) { - list_table old_ref_table(old_list_metadata); + list_table* ref_table = new list_table(old_list_metadata); + old_ref_table = ref_table; for (const auto& x : _data) { - old_ref_table._data.push() = x; + ref_table->_data.push() = x; } for (const auto& x : _entry_state) { - old_ref_table._entry_state.push() = x; + ref_table->_entry_state.push() = x; } _data.clear(); _entry_state.clear(); - impl_init_static_list(permanent_lists); // find best mapping between old and new list elements // + c_ij(value) = min(|v_i - v_j|/Rv,1) // + c_ij(name) = levenshtein, cosine n-grams, jaro-winkler @@ -1060,95 +1064,112 @@ bool list_table::migrate( constexpr float LOW_CONFIDANCE_DROP_PANELTY = 0.6f; float* value_matrix = cost_matrix( MatchListValues{ - old_ref_table._flag_names.data(), old_ref_table._flag_values.data(), - old_ref_table.numFlags() + ref_table->_flag_names.data(), ref_table->_flag_values.data(), ref_table->numFlags() }, MatchListValues{_flag_names.data(), _flag_values.data(), numFlags()}, LOW_CONFIDANCE_DROP_PANELTY ); - const int n_flags = std::max(numFlags(), old_ref_table.numFlags()); - int* value_matches = new int[n_flags]; - algorithms::hungarian_solver(value_matrix, value_matches, n_flags, HIGH_CONFIDANCE_DROP_PANELTY); + const int n_flags = std::max(numFlags(), ref_table->numFlags()); + list_value_matches.resize(n_flags); + algorithms::hungarian_solver( + value_matrix, list_value_matches.data(), n_flags, HIGH_CONFIDANCE_DROP_PANELTY + ); // list matches float* list_matrix = cost_matrix( - MatchList{ - old_ref_table._list_end.data(), old_ref_table._list_names.data(), old_ref_table.numLists() - }, - MatchList{_list_end.data(), _list_names.data(), numLists()}, value_matches, + MatchList{ref_table->_list_end.data(), ref_table->_list_names.data(), ref_table->numLists()}, + MatchList{_list_end.data(), _list_names.data(), numLists()}, list_value_matches.data(), LOW_CONFIDANCE_DROP_PANELTY ); - const int n_lists = std::max(numLists(), old_ref_table.numLists()); - int* list_matches = new int[n_lists]; - algorithms::hungarian_solver(list_matrix, list_matches, n_lists, LOW_CONFIDANCE_DROP_PANELTY); + const int n_lists = std::max(numLists(), ref_table->numLists()); + list_list_matches.resize(n_lists); + algorithms::hungarian_solver( + list_matrix, list_list_matches.data(), n_lists, LOW_CONFIDANCE_DROP_PANELTY + ); // low confidence list_value matches - algorithms::hungarian_solver(value_matrix, value_matches, n_flags, LOW_CONFIDANCE_DROP_PANELTY); - - - for (value& entry : variables.get_all()) { - list old_list = entry.get(); - size_t idx = old_list.lid; - // migrate - list new_list{-1}; - switch (old_ref_table._entry_state[idx]) { - case state::permanent: new_list = create_permament(); break; - case state::used: new_list = create(); break; - default: continue; - } - entry.set(list(new_list)); - - inkAssert(new_list.lid >= 0, "Failed to create new list entry for migration."); - inkAssert( - static_cast(new_list.lid) == idx, - "At position list creation failed with different valid idx." - ); - const data_t* entry = old_ref_table.getPtr(idx); - data_t* new_entry = getPtr(idx); - bool migrated = false; - bool is_empty_list = true; - for (size_t i = 0; i < old_ref_table.numLists(); ++i) { - if (old_ref_table.hasList(entry, i)) { - bool hit = false; - is_empty_list = false; - for (size_t j = old_ref_table.listBegin(i); j < old_ref_table._list_end[i]; ++j) { - if (old_ref_table.hasFlag(entry, j) && old_ref_table._flag_names[j]) { - migrated = false; - if (value_matches[j] != -1) { - hit = true; - migrated = true; - size_t k; - for (k = 0; _list_end[k] <= static_cast(value_matches[j]); ++k) {} - setList(new_entry, k); - setFlag(new_entry, value_matches[j]); - } - } - } - // keep list if list has match but all values where dropped - if (! hit && list_matches[i] != -1) { - setList(new_entry, list_matches[i]); - migrated = true; - } - } - } - // drop list - if (! is_empty_list && ! migrated) { - // FIXME: remove list ? - // _entry_state [idx] = state::empty; - return false; - } - // FIXME: use Assert instead? - // inkAssert(migrated, "Migrating list @%d would lead to an empty list", idx); - } + algorithms::hungarian_solver( + value_matrix, list_list_matches.data(), n_flags, LOW_CONFIDANCE_DROP_PANELTY + ); - delete[] list_matches; - delete[] value_matches; delete[] value_matrix; delete[] list_matrix; return true; } +bool list_table::migrate_variables( + ink::runtime::internal::managed_array& list_old_new_map, + const ink::runtime::internal::managed_array& list_list_matches, + const ink::runtime::internal::managed_array& list_value_matches, + const list_table& old_ref_table, basic_stack& variables +) +{ + // TODO: optimize: map equal permanent values (old list x -> new list x) + bool migration_succeeded = true; + variables.for_each( + [&](entry& value) { + list old_list = value.data.get(); + size_t idx = old_list.lid; + while (list_old_new_map.size() <= idx) { + list_old_new_map.push() = -1; + } + if (list_old_new_map[idx] != -1) { + value.data.set(list(list_old_new_map[idx])); + return; + } + // migrate + list new_list{-1}; + switch (old_ref_table._entry_state[idx]) { + case state::permanent: new_list = create_permament(); break; + case state::used: new_list = create(); break; + default: return; + } + list_old_new_map[idx] = new_list.lid; + value.data.set(new_list); + + inkAssert(new_list.lid >= 0, "Failed to create new list entry for migration."); + const data_t* entry = old_ref_table.getPtr(idx); + data_t* new_entry = getPtr(idx); + bool migrated = false; + bool is_empty_list = true; + for (size_t i = 0; i < old_ref_table.numLists(); ++i) { + if (old_ref_table.hasList(entry, i)) { + bool hit = false; + is_empty_list = false; + for (size_t j = old_ref_table.listBegin(i); j < old_ref_table._list_end[i]; ++j) { + if (old_ref_table.hasFlag(entry, j) && old_ref_table._flag_names[j]) { + migrated = false; + if (list_value_matches[j] != -1) { + hit = true; + migrated = true; + size_t k; + for (k = 0; _list_end[k] <= static_cast(list_value_matches[j]); ++k) {} + setList(new_entry, k); + setFlag(new_entry, list_value_matches[j]); + } + } + } + // keep list if list has match but all values where dropped + if (! hit && list_list_matches[i] != -1) { + setList(new_entry, list_list_matches[i]); + migrated = true; + } + } + } + // drop list + if (! is_empty_list && ! migrated) { + // FIXME: remove list ? + // _entry_state [idx] = state::empty; + migration_succeeded = false; + } + // inkAssert(migrated, "Migrating list @%d would lead to an empty list", idx); + }, + [](const entry& v) { return v.data.type() != value_type::list; } + ); + return migration_succeeded; +} + void list_table::impl_init_static_list(const list_flag* permanent_lists) { const list_flag* flags = permanent_lists; diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h index bf05c3ec..d4d48605 100644 --- a/inkcpp/list_table.h +++ b/inkcpp/list_table.h @@ -117,8 +117,17 @@ class list_table : public snapshot_interface list_table(const char* data); void init_static_list_flags(const list_flag* permanent_lists, basic_stack& variables); // binary list metadata of currently loaded list - bool migrate( - const char* old_list_metadata, const list_flag* permanent_lists, basic_stack& variables + bool create_match_lut( + const char* old_list_metadata, + ink::runtime::internal::managed_array& list_list_matches, + ink::runtime::internal::managed_array& list_value_matches, + const list_table*& old_ref_table + ); + bool migrate_variables( + ink::runtime::internal::managed_array& list_old_new_map, + const ink::runtime::internal::managed_array& list_list_matches, + const ink::runtime::internal::managed_array& list_value_matches, + const list_table& old_ref_table, basic_stack& variables ); explicit list_table() diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 90ec2143..d620c7eb 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -13,6 +13,7 @@ #include "snapshot_impl.h" #include "story_impl.h" #include "system.h" +#include "types.h" #include "value.h" #ifdef INK_ENABLE_STL @@ -662,7 +663,8 @@ bool runner_impl::can_be_migrated() const ? _story->find_container_for(static_cast(_ptr - _story->instructions() - 6)) : ~0U; hash_t c_hash = (container_id != ~0U) ? _story->container_data(container_id)._hash : 0; - // if we are not at the start or terimanet bu we cannot name the current position it is not migratble + // if we are not at the start or terimanet bu we cannot name the current position it is not + // migratble if (c_hash == 0 && _ptr != nullptr && _ptr != _story->instructions()) { return false; } @@ -815,7 +817,7 @@ bool runner_impl::move_to(hash_t path) return true; } -bool runner_impl::migrate_to(hash_t path) +bool runner_impl::migrate_to(const loader& loader, hash_t path) { ip_t destination = _story->find_offset_for(path); if (destination == nullptr) { @@ -854,6 +856,13 @@ bool runner_impl::migrate_to(hash_t path) _container.clear(); _ptr = nullptr; jump(destination, false, true); + + if (loader.old_ref_table && ! _globals->lists().migrate_variables( + loader.list_old_new_map, loader.list_list_matches, loader.list_value_matches, + *loader.old_ref_table, _stack + )) { + return false; + } return true; } diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 74ca4e9c..b01a4aad 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -132,7 +132,7 @@ class runner_impl virtual bool move_to(hash_t path) override; // move to path but keep as much state as possible - bool migrate_to(hash_t path); + bool migrate_to(const loader& loader, hash_t path); #if defined(INK_ENABLE_STL) || defined(INK_ENABLE_UNREAL) // Gets a single line of output diff --git a/inkcpp/snapshot_impl.cpp b/inkcpp/snapshot_impl.cpp index 7c6f2a3a..dd3b9061 100644 --- a/inkcpp/snapshot_impl.cpp +++ b/inkcpp/snapshot_impl.cpp @@ -52,6 +52,16 @@ void snapshot::write_to_file(const char* filename) const namespace ink::runtime::internal { +snapshot_impl::~snapshot_impl() +{ + if (_managed) { + delete[] _file; + } + if (old_ref_table) { + delete old_ref_table; + } +}; + size_t snapshot_impl::file_size(size_t serialization_length, size_t runner_cnt, bool list_definition) { diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h index 2c684df2..8dfc7bd6 100644 --- a/inkcpp/snapshot_impl.h +++ b/inkcpp/snapshot_impl.h @@ -75,15 +75,16 @@ static_assert(sizeof(snap_tag) == sizeof(const char*)); class snapshot_impl final : public snapshot { public: - ~snapshot_impl() override - { - if (_managed) { - delete[] _file; - } - }; + ~snapshot_impl() override; managed_array& strings() const { return string_table; } + managed_array& list_old_new_map() const { return list_old_new_map_table; } + + managed_array& list_list_matches() const { return list_list_matches_table; } + + managed_array& list_value_matches() const { return list_value_matches_table; } + const unsigned char* get_data() const override; size_t get_data_len() const override; @@ -108,11 +109,15 @@ class snapshot_impl final : public snapshot hash_t hash() const { return _header.hash; } + mutable const list_table* old_ref_table = nullptr; private: // file information // only populated when loading snapshots mutable managed_array string_table; + mutable managed_array list_old_new_map_table; + mutable managed_array list_list_matches_table; + mutable managed_array list_value_matches_table; const unsigned char* _file; size_t _length; bool _managed; diff --git a/inkcpp/snapshot_interface.h b/inkcpp/snapshot_interface.h index d47e15e0..3e8bd306 100644 --- a/inkcpp/snapshot_interface.h +++ b/inkcpp/snapshot_interface.h @@ -18,6 +18,7 @@ class managed_array; class snap_tag; class string_table; class value; +class list_table; class snapshot_interface { @@ -70,14 +71,25 @@ class snapshot_interface const char* story_string_table; const bool migratable = false; const snap_tag* runner_tags = nullptr; + managed_array& list_old_new_map; + managed_array& list_list_matches; + managed_array& list_value_matches; + const list_table*& old_ref_table; loader( managed_array& string_table, const char* story_string_table, - bool migratable + managed_array& list_old_new_map, + managed_array& list_list_matches, + managed_array& list_value_matches, bool migratable, + const list_table*& old_ref_table ) : string_table{string_table} , story_string_table{story_string_table} , migratable(migratable) + , list_old_new_map(list_old_new_map) + , list_list_matches(list_list_matches) + , list_value_matches(list_value_matches) + , old_ref_table(old_ref_table) { } diff --git a/inkcpp/stack.h b/inkcpp/stack.h index 1308b71e..e6a87b7a 100644 --- a/inkcpp/stack.h +++ b/inkcpp/stack.h @@ -33,6 +33,7 @@ namespace runtime class basic_stack : protected restorable { + friend list_table; protected: basic_stack(entry* data, size_t size); diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index 1084fa95..986a584f 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -190,14 +190,17 @@ globals story_impl::new_globals_from_snapshot(const snapshot& data) } globals globs = new_globals(); snapshot.strings().clear(); - snapshot_interface::loader loader(snapshot.strings(), _string_table, snapshot.can_be_migrated()); - auto end = globs.cast()->snap_load(snapshot.get_globals_snap(), loader); + snapshot_interface::loader loader( + snapshot.strings(), _string_table, snapshot.list_list_matches(), snapshot.list_old_new_map(), + snapshot.list_value_matches(), snapshot.can_be_migrated(), snapshot.old_ref_table + ); + auto end = globs.cast()->snap_load(snapshot.get_globals_snap(), loader); inkAssert(end == snapshot.get_runner_snap(0), "not all data were used for global reconstruction"); if (hash() != snapshot.hash()) { globals new_globs = new_globals(); runner thread = new_runner(new_globs); if (! globs.cast()->migrate_new_globals( - *new_globs.cast().get(), + loader, *new_globs.cast().get(), reinterpret_cast(snapshot.get_list_metadata()) )) { return globals(); @@ -221,11 +224,15 @@ runner story_impl::new_runner_from_snapshot(const snapshot& data, globals store, runner run(new runner_impl(this, store), _block); // snapshot id is inverso of creation time, but creation time is the more intouitve numbering to // use - idx = (data.num_runners() - idx - 1); + idx = (data.num_runners() - idx - 1); snapshot_interface::loader loader{ snapshot.strings(), _string_table, + snapshot.list_old_new_map(), + snapshot.list_list_matches(), + snapshot.list_value_matches(), snapshot.can_be_migrated(), + snapshot.old_ref_table }; auto end = run.cast()->snap_load(snapshot.get_runner_snap(idx), loader); inkAssert( @@ -239,7 +246,7 @@ runner story_impl::new_runner_from_snapshot(const snapshot& data, globals store, if (current_node == 0) { return run; } - if (! run.cast()->migrate_to(current_node)) { + if (! run.cast()->migrate_to(loader, current_node)) { return runner(); } } diff --git a/inkcpp_python/pybind11 b/inkcpp_python/pybind11 index 1b499083..0c69e1eb 160000 --- a/inkcpp_python/pybind11 +++ b/inkcpp_python/pybind11 @@ -1 +1 @@ -Subproject commit 1b4990838904501de7110d27e96c0a4152029156 +Subproject commit 0c69e1eb2177fa8f8580632c7b1f97fdb606ce8f diff --git a/inkcpp_test/MultiRunner.cpp b/inkcpp_test/MultiRunner.cpp index 99b48962..1201d763 100644 --- a/inkcpp_test/MultiRunner.cpp +++ b/inkcpp_test/MultiRunner.cpp @@ -57,25 +57,39 @@ SCENARIO("UE example story with multiple runner") main_thread->choose(1); THEN("Inventory should be as expected") { - ink::optional inventory = base_globals->get("Inventory"); - REQUIRE(inventory); - list inventory_list = inventory.value().get(); - list_interface::iterator list_iter = inventory_list->begin(); - REQUIRE(list_iter != inventory_list->end()); - list_interface::iterator::Flag flag = *list_iter; - REQUIRE(flag.flag_name == std::string("Skull")); - REQUIRE(flag.list_name == std::string("Clues")); - ++list_iter; - REQUIRE(list_iter != inventory_list->end()); - flag = *list_iter; - REQUIRE(flag.flag_name == std::string("TalkWithAnimals")); - REQUIRE(flag.list_name == std::string("Potions")); + { + + ink::optional inventory = base_globals->get("Inventory"); + REQUIRE(inventory); + list inventory_list = inventory.value().get(); + list_interface::iterator list_iter = inventory_list->begin(); + REQUIRE(list_iter != inventory_list->end()); + list_interface::iterator::Flag flag = *list_iter; + REQUIRE(flag.flag_name == std::string("Skull")); + REQUIRE(flag.list_name == std::string("Clues")); + ++list_iter; + REQUIRE(list_iter != inventory_list->end()); + flag = *list_iter; + REQUIRE(flag.flag_name == std::string("TalkWithAnimals")); + REQUIRE(flag.list_name == std::string("Potions")); + } + { + ink::optional knowladge = base_globals->get("Knowladge"); + REQUIRE(knowladge); + list knowlagde_flag = knowladge.value().get(); + list_interface::iterator list_iter = knowlagde_flag->begin(); + REQUIRE(list_iter != knowlagde_flag->end()); + list_interface::iterator::Flag flag = *list_iter; + REQUIRE(flag.flag_name == std::string("YellowDress")); + REQUIRE(flag.list_name == std::string("Knowladge")); + ++list_iter; + REQUIRE(list_iter == knowlagde_flag->end()); + } } std::unique_ptr snap{base_globals->create_snapshot()}; globals globals_v2 = story_v2->new_globals_from_snapshot(*snap); - runner main_thread_v2 = story_v2->new_runner_from_snapshot(*snap, globals_v2, 1); - REQUIRE(main_thread_v2->getall() == "\"Ahh\", you cry while reaching for the door bell. Saying it was charched would be an understatement.\n"); + runner main_thread_v2 = story_v2->new_runner_from_snapshot(*snap, globals_v2, 1); THEN("Inventory should be still the same") { @@ -104,7 +118,7 @@ SCENARIO("UE example story with multiple runner") REQUIRE_FALSE(side_thread_v2->can_continue()); REQUIRE_FALSE(side_thread_v2->has_choices()); - THEN("We should now can talk with Animals") + // THEN("We should now can talk with Animals") { { ink::optional state = globals_v2->get("StatusConditions"); @@ -130,8 +144,25 @@ SCENARIO("UE example story with multiple runner") ++list_iter; REQUIRE(list_iter == prototype_flag->end()); } - } + { + ink::optional knowladge = globals_v2->get("Knowladge"); + REQUIRE(knowladge); + list knowlagde_flag = knowladge.value().get(); + list_interface::iterator list_iter = knowlagde_flag->begin(); + REQUIRE(list_iter != knowlagde_flag->end()); + list_interface::iterator::Flag flag = *list_iter; + REQUIRE(flag.flag_name == std::string()); + REQUIRE(flag.list_name == std::string()); + ++list_iter; + REQUIRE(list_iter == knowlagde_flag->end()); + } + } + REQUIRE(main_thread_v2->getall() == "\"Ahh\", you cry while reaching for the door bell. Saying it was charched would be an understatement.\n"); + REQUIRE(main_thread_v2->num_choices() == 3); + REQUIRE(main_thread_v2->get_choice(0)->text() == std::string("look around")); + REQUIRE(main_thread_v2->get_choice(1)->text() == std::string("Knock again?")); + REQUIRE(main_thread_v2->get_choice(2)->text() == std::string()); main_thread_v2->choose(2); REQUIRE(main_thread_v2->getall() == "You just saw someone enter, how did they do not get shoked?\nSomething hushes through a hole beside the door, after you come closer you see it. A little gray mouse, it looks quite eloquent.\n"); main_thread_v2->choose(0); From 43735cb8475800be2edb6aac5c992109271b1f4d Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 4 Jun 2026 14:37:07 +0200 Subject: [PATCH 13/29] feat(Migration): implement list tables migration for variables --- inkcpp/list_table.cpp | 4 ++-- inkcpp_test/ListMatching.cpp | 5 +++++ inkcpp_test/MultiRunner.cpp | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index 3d7de8ee..43325951 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -1089,7 +1089,7 @@ bool list_table::create_match_lut( // low confidence list_value matches algorithms::hungarian_solver( - value_matrix, list_list_matches.data(), n_flags, LOW_CONFIDANCE_DROP_PANELTY + value_matrix, list_value_matches.data(), n_flags, LOW_CONFIDANCE_DROP_PANELTY ); @@ -1130,7 +1130,7 @@ bool list_table::migrate_variables( inkAssert(new_list.lid >= 0, "Failed to create new list entry for migration."); const data_t* entry = old_ref_table.getPtr(idx); - data_t* new_entry = getPtr(idx); + data_t* new_entry = getPtr(new_list.lid); bool migrated = false; bool is_empty_list = true; for (size_t i = 0; i < old_ref_table.numLists(); ++i) { diff --git a/inkcpp_test/ListMatching.cpp b/inkcpp_test/ListMatching.cpp index d29eb6ef..1d3cb19a 100644 --- a/inkcpp_test/ListMatching.cpp +++ b/inkcpp_test/ListMatching.cpp @@ -178,6 +178,7 @@ SCENARIO("find best assigments", "[list_match][hungarian]") SCENARIO("Simple List Migration stories", "[list_match]") { + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF); GIVEN("Splitted List") { std::unique_ptr ink_a{story::from_file(INK_TEST_RESOURCE_DIR "ListMatchStoryA.bin")}; @@ -195,8 +196,12 @@ SCENARIO("Simple List Migration stories", "[list_match]") thread_a->getall() == "More\nYou are still at Flor, Balcony - all posibilities are Flor, Balcony, Kitchen, Garden\n" ); + + auto globals_b = ink_b->new_globals_from_snapshot(*snap); + auto thread_b = ink_b->new_runner_from_snapshot(*snap, globals_b); + CHECK( thread_b->getall() == "More\nYou are still at Floor, Balcony - all posibilities are Kitchen, Street, Floor, Balcony, Livingroom, Garden\n" diff --git a/inkcpp_test/MultiRunner.cpp b/inkcpp_test/MultiRunner.cpp index 1201d763..c7e63d12 100644 --- a/inkcpp_test/MultiRunner.cpp +++ b/inkcpp_test/MultiRunner.cpp @@ -151,8 +151,8 @@ SCENARIO("UE example story with multiple runner") list_interface::iterator list_iter = knowlagde_flag->begin(); REQUIRE(list_iter != knowlagde_flag->end()); list_interface::iterator::Flag flag = *list_iter; - REQUIRE(flag.flag_name == std::string()); - REQUIRE(flag.list_name == std::string()); + REQUIRE(flag.flag_name == std::string("YellowDress")); + REQUIRE(flag.list_name == std::string("Knowladge")); ++list_iter; REQUIRE(list_iter == knowlagde_flag->end()); @@ -162,7 +162,7 @@ SCENARIO("UE example story with multiple runner") REQUIRE(main_thread_v2->num_choices() == 3); REQUIRE(main_thread_v2->get_choice(0)->text() == std::string("look around")); REQUIRE(main_thread_v2->get_choice(1)->text() == std::string("Knock again?")); - REQUIRE(main_thread_v2->get_choice(2)->text() == std::string()); + REQUIRE(main_thread_v2->get_choice(2)->text() == std::string("Inspect the Door")); main_thread_v2->choose(2); REQUIRE(main_thread_v2->getall() == "You just saw someone enter, how did they do not get shoked?\nSomething hushes through a hole beside the door, after you come closer you see it. A little gray mouse, it looks quite eloquent.\n"); main_thread_v2->choose(0); From ae2a474e10d67832091d82e2de831e0cbc84f334 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 4 Jun 2026 20:28:40 +0200 Subject: [PATCH 14/29] docs: Add documentation for new UE components, bump Doxygen to 1.17 also document undocumented elements --- Documentation/unreal/imgs/SaveGame.png | Bin 0 -> 148794 bytes Doxyfile | 829 ++++++++++++------ inkcpp/include/list.h | 3 + inkcpp/include/runner.h | 18 +- inkcpp/include/snapshot.h | 12 +- inkcpp/include/story.h | 3 +- inkcpp/include/types.h | 18 +- inkcpp/list_table.cpp | 7 + inkcpp/list_table.h | 2 +- inkcpp/runner_impl.cpp | 49 +- inkcpp_c/include/inkcpp.h | 4 +- inkcpp_compiler/binary_emitter.cpp | 20 +- inkcpp_test/ListMatching.cpp | 5 +- shared/public/config.h | 118 +-- shared/public/system.h | 31 +- unreal/blueprint_filter.js | 32 +- .../inkcpp/Source/inkcpp/Public/InkSnapshot.h | 58 +- unreal/inkcpp/Source/inkcpp/Public/inkcpp.h | 23 +- 18 files changed, 803 insertions(+), 429 deletions(-) create mode 100644 Documentation/unreal/imgs/SaveGame.png diff --git a/Documentation/unreal/imgs/SaveGame.png b/Documentation/unreal/imgs/SaveGame.png new file mode 100644 index 0000000000000000000000000000000000000000..493cb8c3833e63b85874cccd531b3e32fac50b83 GIT binary patch literal 148794 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 literal 0 HcmV?d00001 diff --git a/Doxyfile b/Doxyfile index ded1987f..966bc55f 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,7 +1,7 @@ -# Doxyfile 1.9.8 +# Doxyfile 1.17.0 # This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. +# Doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. @@ -15,10 +15,10 @@ # # Note: # -# Use doxygen to compare the used configuration file with the template +# Use Doxygen to compare the used configuration file with the template # configuration file: # doxygen -x [configFile] -# Use doxygen to compare the used configuration file with the template +# Use Doxygen to compare the used configuration file with the template # configuration file without replacing the environment variables or CMake type # replacement variables: # doxygen -x_noenv [configFile] @@ -51,7 +51,7 @@ PROJECT_NAME = inkcpp PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a +# for a project that appears at the top of each page and should give viewers a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = @@ -63,19 +63,25 @@ PROJECT_BRIEF = PROJECT_LOGO = +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = + # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If +# entered, it will be relative to the location where Doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = Documentation -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096 # sub-directories (in 2 levels) under the output directory of each output format # and will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# option can be useful when feeding Doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise cause +# performance problems for the file system. Adjust CREATE_SUBDIRS_LEVEL to # control the number of sub-directories. # The default value is: NO. @@ -92,7 +98,7 @@ CREATE_SUBDIRS = NO CREATE_SUBDIRS_LEVEL = 8 -# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. @@ -101,7 +107,7 @@ CREATE_SUBDIRS_LEVEL = 8 ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this +# documentation generated by Doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, # Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English @@ -115,14 +121,14 @@ ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the @@ -153,13 +159,13 @@ ABBREVIATE_BRIEF = "The $name class" \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief +# Doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. @@ -167,7 +173,7 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. @@ -177,11 +183,11 @@ FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to +# If left blank the directory from which Doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. +# will be relative from the directory where Doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = @@ -195,41 +201,42 @@ STRIP_FROM_PATH = STRIP_FROM_INC_PATH = -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't +# If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but +# less readable) file names. This can be useful if your file system doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the +# first line (until the first dot, question mark or exclamation mark) of a +# Javadoc-style comment as the brief description. If set to NO, the Javadoc- +# style will behave just like regular Qt-style comments (thus requiring an +# explicit @brief command for a brief description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES -# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be -# interpreted by doxygen. +# interpreted by Doxygen. # The default value is: NO. JAVADOC_BANNER = NO -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first +# line (until the first dot, question mark or exclamation mark) of a Qt-style +# comment as the brief description. If set to NO, the Qt-style will behave just +# like regular Qt-style comments (thus requiring an explicit \brief command for +# a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this @@ -241,10 +248,10 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO -# By default Python docstrings are displayed as preformatted text and doxygen's +# By default Python docstrings are displayed as preformatted text and Doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the -# doxygen's special commands can be used and the contents of the docstring -# documentation blocks is shown as doxygen documentation. +# Doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as Doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES @@ -255,7 +262,7 @@ PYTHON_DOCSTRING = YES INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. @@ -325,40 +332,54 @@ OPTIMIZE_OUTPUT_SLICE = NO # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# language is one of the parsers supported by Doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, # VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the -# default for Fortran type files). For instance to make doxygen treat .inc files +# default for Fortran type files). For instance to make Doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. When specifying no_extension you should add +# the files are not read by Doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# The output of markdown processing is further processed by Doxygen, so you can +# mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES +# If the MARKDOWN_STRICT tag is enabled then Doxygen treats text in comments as +# Markdown formatted also in cases where Doxygen's native markup format +# conflicts with that of Markdown. This is only relevant in cases where +# backticks are used. Doxygen's native markup style allows a single quote to end +# a text fragment started with a backtick and then treat it as a piece of quoted +# text, whereas in Markdown such text fragment is treated as verbatim and only +# ends when a second matching backtick is found. Also, Doxygen's native markup +# format requires double quotes to be escaped when they appear in a backtick +# section, whereas this is not needed for Markdown. +# The default value is: YES. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_STRICT = YES + # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 5. +# Minimum value: 0, maximum value: 99, default value: 6. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 5 @@ -374,20 +395,29 @@ TOC_INCLUDE_HEADINGS = 5 MARKDOWN_ID_STYLE = DOXYGEN -# When enabled doxygen tries to link words that correspond to documented +# When enabled Doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. +# globally by setting AUTOLINK_SUPPORT to NO. Words listed in the +# AUTOLINK_IGNORE_WORDS tag are excluded from automatic linking. # The default value is: YES. AUTOLINK_SUPPORT = YES +# This tag specifies a list of words that, when matching the start of a word in +# the documentation, will suppress auto links generation, if it is enabled via +# AUTOLINK_SUPPORT. This list does not affect links explicitly created using # +# or the \link or \ref commands. +# This tag requires that the tag AUTOLINK_SUPPORT is set to YES. + +AUTOLINK_IGNORE_WORDS = + # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and +# tag to YES in order to let Doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. +# versus func(std::string) {}). This also makes the inheritance and +# collaboration diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO @@ -399,16 +429,16 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. +# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse +# them like normal C++ but will assume all classes use public instead of private +# inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. +# Doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. @@ -417,7 +447,7 @@ SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES then doxygen will reuse the documentation of the first +# tag is set to YES then Doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. @@ -475,18 +505,18 @@ TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The +# code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# Doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest +# symbols. At the end of a run Doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 -# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use -# during processing. When set to 0 doxygen will based this on the number of +# The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use +# during processing. When set to 0 Doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple @@ -494,7 +524,7 @@ LOOKUP_CACHE_SIZE = 0 # which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. -# Minimum value: 0, maximum value: 32, default value: 1. +# Minimum value: 0, maximum value: 512, default value: 1. NUM_PROC_THREADS = 1 @@ -510,7 +540,7 @@ TIMESTAMP = NO # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. @@ -576,7 +606,7 @@ EXTRACT_ANON_NSPACES = NO RESOLVE_UNNAMED_PARAMS = YES -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. @@ -584,7 +614,7 @@ RESOLVE_UNNAMED_PARAMS = YES HIDE_UNDOC_MEMBERS = NO -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # will also hide undocumented C++ concepts if enabled. This option has no effect @@ -593,14 +623,22 @@ HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# If the HIDE_UNDOC_NAMESPACES tag is set to YES, Doxygen will hide all +# undocumented namespaces that are normally visible in the namespace hierarchy. +# If set to NO, these namespaces will be included in the various overviews. This +# option has no effect if EXTRACT_ALL is enabled. +# The default value is: YES. + +HIDE_UNDOC_NAMESPACES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. @@ -614,7 +652,7 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# With the correct setting of option CASE_SENSE_NAMES Doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly @@ -623,7 +661,7 @@ INTERNAL_DOCS = NO # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On -# Windows (including Cygwin) and MacOS, users should typically set this option +# Windows (including Cygwin) and macOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # Possible values are: SYSTEM, NO and YES. @@ -631,14 +669,14 @@ INTERNAL_DOCS = NO CASE_SENSE_NAMES = NO -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO -# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. @@ -651,7 +689,7 @@ HIDE_COMPOUND_REFERENCE= NO SHOW_HEADERFILE = YES -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -664,7 +702,7 @@ SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = NO -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. @@ -676,14 +714,14 @@ FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. @@ -691,7 +729,7 @@ SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. @@ -703,7 +741,7 @@ SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. @@ -720,11 +758,11 @@ SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. @@ -755,6 +793,27 @@ GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES +# The GENERATE_REQUIREMENTS tag can be used to enable (YES) or disable (NO) the +# requirements page. When enabled, this page is automatically created when at +# least one comment block with a \requirement command appears in the input. +# The default value is: YES. + +GENERATE_REQUIREMENTS = YES + +# The REQ_TRACEABILITY_INFO tag controls if traceability information is shown on +# the requirements page (only relevant when using \requirement comment blocks). +# The setting NO will disable the traceability information altogether. The +# setting UNSATISFIED_ONLY will show a list of requirements that are missing a +# satisfies relation (through the command: \satisfies). Similarly the setting +# UNVERIFIED_ONLY will show a list of requirements that are missing a verifies +# relation (through the command: \verifies). Setting the tag to YES (the +# default) will show both lists if applicable. +# Possible values are: YES, NO, UNSATISFIED_ONLY and UNVERIFIED_ONLY. +# The default value is: YES. +# This tag requires that the tag GENERATE_REQUIREMENTS is set to YES. + +REQ_TRACEABILITY_INFO = YES + # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. @@ -794,25 +853,25 @@ SHOW_FILES = YES SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from +# Doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file +# by Doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated +# by Doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can +# that represents Doxygen's defaults, run Doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. See also section "Changing the # layout of pages" for information. # -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# Note that if you run Doxygen from a directory containing a file called +# DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = @@ -827,19 +886,35 @@ LAYOUT_FILE = CITE_BIB_FILES = +# The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH +# environment variable) so that external tools such as latex and gs can be +# found. +# Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the +# path already specified by the PATH variable, and are added in the order +# specified. +# Note: This option is particularly useful for macOS version 14 (Sonoma) and +# higher, when running Doxygen from Doxywizard, because in this case any user- +# defined changes to the PATH are ignored. A typical example on macOS is to set +# EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin +# together with the standard path, the full search path used by doxygen when +# launching external tools will then become +# PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + +EXTERNAL_TOOL_PATH = + #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the +# standard output by Doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. @@ -847,14 +922,14 @@ QUIET = YES WARNINGS = YES -# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for # potential errors in the documentation, such as documenting some parameters in # a documented function twice, or documenting parameters that don't exist or # using markup commands wrongly. @@ -862,8 +937,8 @@ WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES -# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete -# function parameter documentation. If set to NO, doxygen will accept that some +# If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete +# function parameter documentation. If set to NO, Doxygen will accept that some # parameters have no documentation without warning. # The default value is: YES. @@ -871,7 +946,7 @@ WARN_IF_INCOMPLETE_DOC = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong parameter +# value. If set to NO, Doxygen will only warn about wrong parameter # documentation, but not about the absence of documentation. If EXTRACT_ALL is # set to YES then this flag will automatically be disabled. See also # WARN_IF_INCOMPLETE_DOC @@ -879,20 +954,28 @@ WARN_IF_INCOMPLETE_DOC = YES WARN_NO_PARAMDOC = NO -# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about -# undocumented enumeration values. If set to NO, doxygen will accept +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about +# undocumented enumeration values. If set to NO, Doxygen will accept # undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: NO. WARN_IF_UNDOC_ENUM_VAL = NO -# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# If WARN_LAYOUT_FILE option is set to YES, Doxygen will warn about issues found +# while parsing the user defined layout file, such as missing or wrong elements. +# See also LAYOUT_FILE for details. If set to NO, problems with the layout file +# will be suppressed. +# The default value is: YES. + +WARN_LAYOUT_FILE = YES + +# If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS -# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but -# at the end of the doxygen process doxygen will return with a non-zero status. -# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves -# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the Doxygen process Doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not # write the warning messages in between other messages but write them at the end # of a run, in case a WARN_LOGFILE is defined the warning messages will be # besides being in the defined file also be shown at the end of a run, unless @@ -903,7 +986,7 @@ WARN_IF_UNDOC_ENUM_VAL = NO WARN_AS_ERROR = NO -# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# The WARN_FORMAT tag determines the format of the warning messages that Doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will @@ -916,7 +999,7 @@ WARN_FORMAT = "$file:$line: $text" # In the $text part of the WARN_FORMAT command it is possible that a reference # to a more specific place is given. To make it easier to jump to this place -# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# (outside of Doxygen) the user can define a custom "cut" / "paste" string. # Example: # WARN_LINE_FORMAT = "'vi $file +$line'" # See also: WARN_FORMAT @@ -950,7 +1033,7 @@ INPUT = inkcpp/include \ inkcpp_c/include # This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. @@ -960,12 +1043,12 @@ INPUT = inkcpp/include \ INPUT_ENCODING = UTF-8 # This tag can be used to specify the character encoding of the source files -# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# that Doxygen parses. The INPUT_FILE_ENCODING tag can be used to specify # character encoding on a per file pattern basis. Doxygen will compare the file # name with each pattern and apply the encoding instead of the default -# INPUT_ENCODING) if there is a match. The character encodings are a list of the -# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding -# "INPUT_ENCODING" for further information on supported encodings. +# INPUT_ENCODING if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). +# See also: INPUT_ENCODING for further information on supported encodings. INPUT_FILE_ENCODING = @@ -975,16 +1058,16 @@ INPUT_FILE_ENCODING = # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not -# read by doxygen. +# read by Doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, -# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, -# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, # *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be -# provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, # *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ @@ -1043,7 +1126,7 @@ RECURSIVE = NO # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # -# Note that relative paths are relative to the directory from which doxygen is +# Note that relative paths are relative to the directory from which Doxygen is # run. EXCLUDE = @@ -1070,7 +1153,9 @@ EXCLUDE_PATTERNS = # wildcard * is used, a substring. Examples: ANamespace, AClass, # ANamespace::AClass, ANamespace::*Test -EXCLUDE_SYMBOLS = ink::list_flag ink::runtime::internal* ink::internal* +EXCLUDE_SYMBOLS = ink::list_flag \ + ink::runtime::internal* \ + ink::internal* # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include @@ -1098,7 +1183,7 @@ EXAMPLE_RECURSIVE = NO IMAGE_PATH = -# The INPUT_FILTER tag can be used to specify a program that doxygen should +# The INPUT_FILTER tag can be used to specify a program that Doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # @@ -1113,14 +1198,14 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # -# Note that doxygen will use the data processed and written to standard output +# Note that Doxygen will use the data processed and written to standard output # for further processing, therefore nothing else, like debug statements or used # commands (so in case of a Windows batch file always use @echo OFF), should be # written to standard output. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. +# properly processed by Doxygen. INPUT_FILTER = @@ -1133,7 +1218,7 @@ INPUT_FILTER = # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. +# properly processed by Doxygen. FILTER_PATTERNS = @@ -1155,10 +1240,19 @@ FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. +# and want to reuse the introduction page also for the Doxygen output. USE_MDFILE_AS_MAINPAGE = +# If the IMPLICIT_DIR_DOCS tag is set to YES, any README.md file found in sub- +# directories of the project's root, is used as the documentation for that sub- +# directory, except when the README.md starts with a \dir, \page or \mainpage +# command. If set to NO, the README.md file needs to start with an explicit \dir +# command in order to be used as directory documentation. +# The default value is: YES. + +IMPLICIT_DIR_DOCS = YES + # The Fortran standard specifies that for fixed formatted Fortran code all # characters from position 72 are to be considered as comment. A common # extension is to allow longer lines before the automatic comment starts. The @@ -1182,12 +1276,13 @@ FORTRAN_COMMENT_AFTER = 72 SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. +# multi-line macros, enums or list initialized variables directly into the +# documentation. # The default value is: NO. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. @@ -1225,7 +1320,7 @@ REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# point to the HTML generated by the htags(1) tool instead of Doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. @@ -1239,14 +1334,14 @@ SOURCE_TOOLTIPS = YES # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # -# The result: instead of the source browser generated by doxygen, the links to +# The result: instead of the source browser generated by Doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. @@ -1254,6 +1349,46 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then Doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which Doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not Doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then Doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by Doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not Doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1278,7 +1413,7 @@ IGNORE_PREFIX = # Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES @@ -1299,40 +1434,40 @@ HTML_OUTPUT = html HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a +# each generated HTML page. If the tag is left blank Doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. +# that Doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally +# for information on how to generate the default header that Doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description +# default header when upgrading to a newer version of Doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard +# generated HTML page. If the tag is left blank Doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. +# that Doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. +# the HTML output. If left blank Doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. +# sheet that Doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. @@ -1342,7 +1477,7 @@ HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. +# created by Doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. @@ -1370,11 +1505,11 @@ HTML_EXTRA_FILES = Documentation/inkcpp_py.html # The HTML_COLORSTYLE tag can be used to specify if the generated HTML output # should be rendered with a dark or light theme. -# Possible values are: LIGHT always generate light mode output, DARK always -# generate dark mode output, AUTO_LIGHT automatically set the mode according to -# the user preference, use light mode if no preference is set (the default), -# AUTO_DARK automatically set the mode according to the user preference, use -# dark mode if no preference is set and TOGGLE allow to user to switch between +# Possible values are: LIGHT always generates light mode output, DARK always +# generates dark mode output, AUTO_LIGHT automatically sets the mode according +# to the user preference, uses light mode if no preference is set (the default), +# AUTO_DARK automatically sets the mode according to the user preference, uses +# dark mode if no preference is set and TOGGLE allows a user to switch between # light and dark mode via a button. # The default value is: AUTO_LIGHT. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1437,6 +1572,26 @@ HTML_DYNAMIC_SECTIONS = NO HTML_CODE_FOLDING = YES +# If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to @@ -1454,7 +1609,7 @@ HTML_INDEX_NUM_ENTRIES = 32 # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To -# create a documentation set, doxygen will generate a Makefile in the HTML +# create a documentation set, Doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at @@ -1502,18 +1657,18 @@ DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # on Windows. In the beginning of 2021 Microsoft took the original page, with -# a.o. the download links, offline the HTML help workshop was already many years -# in maintenance mode). You can download the HTML help workshop from the web -# archives at Installation executable (see: -# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo -# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# a.o. the download links, offline (the HTML help workshop was already many +# years in maintenance mode). You can download the HTML help workshop from the +# web archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/https://download.microsoft.com/downl +# oad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for @@ -1533,7 +1688,7 @@ CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, -# doxygen will try to run the HTML help compiler on the generated index.hhp. +# Doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1635,7 +1790,7 @@ QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path -# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1680,29 +1835,38 @@ DISABLE_INDEX = NO # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine tune the look of the index (see "Fine-tuning the output"). As an -# example, the default style sheet generated by doxygen has an example that +# example, the default style sheet generated by Doxygen has an example that # shows how to put an image at the root of the tree instead of the PROJECT_NAME. -# Since the tree basically has the same information as the tab index, you could -# consider setting DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. +# Since the tree basically has more details information than the tab index, you +# could consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES -# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the -# FULL_SIDEBAR option determines if the side bar is limited to only the treeview -# area (value NO) or if it should extend to the full height of the window (value -# YES). Setting this to YES gives a layout similar to -# https://docs.readthedocs.io with more room for contents, but less room for the -# project logo, title, and description. If either GENERATE_TREEVIEW or -# DISABLE_INDEX is set to NO, this option has no effect. +# When GENERATE_TREEVIEW is set to YES, the PAGE_OUTLINE_PANEL option determines +# if an additional navigation panel is shown at the right hand side of the +# screen, displaying an outline of the contents of the main page, similar to +# e.g. https://developer.android.com/reference If GENERATE_TREEVIEW is set to +# NO, this option has no effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +PAGE_OUTLINE_PANEL = YES + +# When GENERATE_TREEVIEW is set to YES, the FULL_SIDEBAR option determines if +# the side bar is limited to only the treeview area (value NO) or if it should +# extend to the full height of the window (value YES). Setting this to YES gives +# a layout similar to e.g. https://docs.readthedocs.io with more room for +# contents, but less room for the project logo, title, and description. If +# GENERATE_TREEVIEW is set to NO, this option has no effect. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. FULL_SIDEBAR = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. +# Doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. @@ -1711,6 +1875,12 @@ FULL_SIDEBAR = NO ENUM_VALUES_PER_LINE = 4 +# When the SHOW_ENUM_VALUES tag is set doxygen will show the specified +# enumeration values besides the enumeration mnemonics. +# The default value is: NO. + +SHOW_ENUM_VALUES = NO + # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. @@ -1718,21 +1888,21 @@ ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 -# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO -# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email # addresses. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. OBFUSCATE_EMAILS = YES -# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. @@ -1745,7 +1915,7 @@ HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML +# Doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1774,7 +1944,7 @@ USE_MATHJAX = NO # regards to the different settings, so it is possible that also other MathJax # settings have to be changed when switching between the different MathJax # versions. -# Possible values are: MathJax_2 and MathJax_3. +# Possible values are: MathJax_2, MathJax_3 and MathJax_4. # The default value is: MathJax_2. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1783,13 +1953,14 @@ MATHJAX_VERSION = MathJax_2 # When MathJax is enabled you can set the default output format to be used for # the MathJax output. For more details about the output format see MathJax # version 2 (see: -# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# https://docs.mathjax.org/en/v2.7/output.html), MathJax version 3 (see: +# https://docs.mathjax.org/en/v3.2/output/index.html) and MathJax version 4 # (see: -# http://docs.mathjax.org/en/latest/web/components/output.html). +# https://docs.mathjax.org/en/v4.0/output/index.htm). # Possible values are: HTML-CSS (which is slower, but has the best # compatibility. This is the name for Mathjax version 2, for MathJax version 3 # this will be translated into chtml), NativeMML (i.e. MathML. Only supported -# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This # is the name for Mathjax version 3, for MathJax version 2 this will be # translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. @@ -1798,46 +1969,60 @@ MATHJAX_VERSION = MathJax_2 MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. The default value is: +# output directory using the MATHJAX_RELPATH option. For Mathjax version 2 the +# destination directory should contain the MathJax.js script. For instance, if +# the mathjax directory is located at the same level as the HTML output +# directory, then MATHJAX_RELPATH should be ../mathjax. For Mathjax versions 3 +# and 4 the destination directory should contain the tex-.js script +# (where is either chtml or svg). The default value points to the +# MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. However, it is strongly recommended to install a local +# copy of MathJax from https://www.mathjax.org before deployment. The default +# value is: # - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 # - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# - in case of MathJax version 4: https://cdn.jsdelivr.net/npm/mathjax@4 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example -# for MathJax version 2 (see -# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# for MathJax version 2 (see https://docs.mathjax.org/en/v2.7/tex.html): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # For example for MathJax version 3 (see -# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# https://docs.mathjax.org/en/v3.2/input/tex/extensions/): # MATHJAX_EXTENSIONS = ams +# For example for MathJax version 4 (see +# https://docs.mathjax.org/en/v4.0/input/tex/extensions/): +# MATHJAX_EXTENSIONS = units +# Note that for Mathjax version 4 quite a few extensions are already +# automatically loaded. To disable a package in Mathjax version 4 one can use +# the package name prepended with a minus sign (- like MATHJAX_EXTENSIONS += +# -textmacros) # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: -# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an -# example see the documentation. +# The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces +# of code that will be used on startup of the MathJax code. See the Mathjax site +# for more details: +# - MathJax version 2 (see: +# https://docs.mathjax.org/en/v2.7/) +# - MathJax version 3 (see: +# https://docs.mathjax.org/en/v3.2/) +# - MathJax version 4 (see: +# https://docs.mathjax.org/en/v4.0/) For an example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and +# When the SEARCHENGINE tag is enabled Doxygen will generate a search box for +# the HTML output. The underlying search engine uses JavaScript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then +# For large projects the JavaScript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically @@ -1856,7 +2041,7 @@ SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH -# setting. When disabled, doxygen will generate a PHP script for searching and +# setting. When disabled, Doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing # and searching needs to be provided by external tools. See the section # "External Indexing and Searching" for details. @@ -1865,7 +2050,7 @@ SEARCHENGINE = YES SERVER_BASED_SEARCH = NO -# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# When EXTERNAL_SEARCH tag is enabled Doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain the @@ -1910,7 +2095,7 @@ SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = -# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through Doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of @@ -1924,7 +2109,7 @@ EXTRA_SEARCH_MAPPINGS = # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# If the GENERATE_LATEX tag is set to YES, Doxygen will generate LaTeX output. # The default value is: YES. GENERATE_LATEX = NO @@ -1969,7 +2154,7 @@ MAKEINDEX_CMD_NAME = makeindex LATEX_MAKEINDEX_CMD = makeindex -# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# If the COMPACT_LATEX tag is set to YES, Doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -2000,15 +2185,15 @@ EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for # the generated LaTeX document. The header should contain everything until the -# first chapter. If it is left blank doxygen will generate a standard header. It +# first chapter. If it is left blank Doxygen will generate a standard header. It # is highly recommended to start with a default header using # doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty # and then modify the file new_header.tex. See also section "Doxygen usage" for -# information on how to generate the default header that doxygen normally uses. +# information on how to generate the default header that Doxygen normally uses. # # Note: Only use a user-defined header if you know what you are doing! # Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. The following +# default header when upgrading to a newer version of Doxygen. The following # commands have a special meaning inside the header (and footer): For a # description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2017,10 +2202,10 @@ LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for # the generated LaTeX document. The footer should contain everything after the -# last chapter. If it is left blank doxygen will generate a standard footer. See +# last chapter. If it is left blank Doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what # special commands can be used inside the footer. See also section "Doxygen -# usage" for information on how to generate the default footer that doxygen +# usage" for information on how to generate the default footer that Doxygen # normally uses. Note: Only use a user-defined footer if you know what you are # doing! # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2029,7 +2214,7 @@ LATEX_FOOTER = # The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined # LaTeX style sheets that are included after the standard style sheets created -# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# by Doxygen. Using this option one can overrule certain style aspects. Doxygen # will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the @@ -2055,7 +2240,7 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# If the USE_PDFLATEX tag is set to YES, Doxygen will use the engine as # specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX # files. Set this option to YES, to get a higher quality PDF documentation. # @@ -2080,7 +2265,7 @@ USE_PDFLATEX = YES LATEX_BATCHMODE = NO -# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# If the LATEX_HIDE_INDICES tag is set to YES then Doxygen will not include the # index chapters (such as File Index, Compound Index, etc.) in the output. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2090,7 +2275,7 @@ LATEX_HIDE_INDICES = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. -# The default value is: plain. +# The default value is: plainnat. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BIB_STYLE = plain @@ -2107,7 +2292,7 @@ LATEX_EMOJI_DIRECTORY = # Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# If the GENERATE_RTF tag is set to YES, Doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. @@ -2122,7 +2307,7 @@ GENERATE_RTF = NO RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# If the COMPACT_RTF tag is set to YES, Doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -2142,28 +2327,36 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's +# Load stylesheet definitions from file. Syntax is similar to Doxygen's # configuration file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the -# default style sheet that doxygen normally uses. +# default style sheet that Doxygen normally uses. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's configuration file. A template extensions file can be +# similar to Doxygen's configuration file. A template extensions file can be # generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = +# The RTF_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the RTF_OUTPUT output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTRA_FILES = + #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# If the GENERATE_MAN tag is set to YES, Doxygen will generate man pages for # classes and files. # The default value is: NO. @@ -2194,7 +2387,7 @@ MAN_EXTENSION = .3 MAN_SUBDIR = -# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, then it # will generate one additional man file for each entity documented in the real # man page(s). These additional files only source the real man page, but without # them the man command would be unable to find the correct page. @@ -2207,7 +2400,7 @@ MAN_LINKS = NO # Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# If the GENERATE_XML tag is set to YES, Doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. @@ -2221,7 +2414,7 @@ GENERATE_XML = NO XML_OUTPUT = xml -# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# If the XML_PROGRAMLISTING tag is set to YES, Doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. @@ -2230,7 +2423,7 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = YES -# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, Doxygen will include # namespace members in file scope as well, matching the HTML output. # The default value is: NO. # This tag requires that the tag GENERATE_XML is set to YES. @@ -2241,7 +2434,7 @@ XML_NS_MEMB_FILE_SCOPE = NO # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- -# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# If the GENERATE_DOCBOOK tag is set to YES, Doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. @@ -2259,7 +2452,7 @@ DOCBOOK_OUTPUT = docbook # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# If the GENERATE_AUTOGEN_DEF tag is set to YES, Doxygen will generate an # AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures # the structure of the code including all documentation. Note that this feature # is still experimental and incomplete at the moment. @@ -2271,8 +2464,8 @@ GENERATE_AUTOGEN_DEF = NO # Configuration options related to Sqlite3 output #--------------------------------------------------------------------------- -# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3 -# database with symbols found by doxygen stored in tables. +# If the GENERATE_SQLITE3 tag is set to YES Doxygen will generate a Sqlite3 +# database with symbols found by Doxygen stored in tables. # The default value is: NO. GENERATE_SQLITE3 = NO @@ -2285,9 +2478,9 @@ GENERATE_SQLITE3 = NO SQLITE3_OUTPUT = sqlite3 -# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db -# database file will be recreated with each doxygen run. If set to NO, doxygen -# will warn if an a database file is already found and not modify it. +# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db +# database file will be recreated with each Doxygen run. If set to NO, Doxygen +# will warn if a database file is already found and not modify it. # The default value is: YES. # This tag requires that the tag GENERATE_SQLITE3 is set to YES. @@ -2297,7 +2490,7 @@ SQLITE3_RECREATE_DB = YES # Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# If the GENERATE_PERLMOD tag is set to YES, Doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. @@ -2305,7 +2498,7 @@ SQLITE3_RECREATE_DB = YES GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# If the PERLMOD_LATEX tag is set to YES, Doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. @@ -2335,13 +2528,13 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# If the ENABLE_PREPROCESSING tag is set to YES, Doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# If the MACRO_EXPANSION tag is set to YES, Doxygen will expand all macro names # in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. @@ -2390,10 +2583,10 @@ INCLUDE_FILE_PATTERNS = # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. PREDEFINED = DOXYGEN \ - INK_ENABLE_STL \ - INK_ENABLE_CSTD \ - INK_ENABLE_UNREAL \ - UFUNCTION(...):= + INK_ENABLE_STL \ + INK_ENABLE_CSTD \ + INK_ENABLE_UNREAL \ + UFUNCTION(...):= # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -2404,7 +2597,7 @@ PREDEFINED = DOXYGEN \ EXPAND_AS_DEFINED = UFUNCTION -# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# If the SKIP_FUNCTION_MACROS tag is set to YES then Doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have # an all uppercase name, and do not end with a semicolon. Such function macros # are typically used for boiler-plate code, and will confuse the parser if not @@ -2428,12 +2621,12 @@ SKIP_FUNCTION_MACROS = YES # section "Linking to external documentation" for more information about the use # of tag files. # Note: Each tag file must have a unique name (where the name does NOT include -# the path). If a tag file is not located in the directory in which doxygen is +# the path). If a tag file is not located in the directory in which Doxygen is # run, you must also specify the path to the tagfile here. TAGFILES = -# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# When a file name is specified after GENERATE_TAGFILE, Doxygen will create a # tag file that is based on the input files it reads. See section "Linking to # external documentation" for more information about the usage of tag files. @@ -2470,7 +2663,7 @@ EXTERNAL_PAGES = YES HIDE_UNDOC_RELATIONS = YES -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# If you set the HAVE_DOT tag to YES then Doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: # https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is @@ -2479,19 +2672,32 @@ HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed -# to run in parallel. When set to 0 doxygen will base this on the number of +# The DOT_NUM_THREADS specifies the number of dot invocations Doxygen is allowed +# to run in parallel. When set to 0 Doxygen will base this on the number of # processors available in the system. You can set it explicitly to a value # larger than 0 to get control over the balance between CPU load and processing # speed. -# Minimum value: 0, maximum value: 32, default value: 0. +# Minimum value: 0, maximum value: 512, default value: 0. # This tag requires that the tag HAVE_DOT is set to YES. DOT_NUM_THREADS = 0 +# The DOT_BATCH_SIZE specifies the number of dot graphs Doxygen is allowed to +# compile in a single invocation of dot. When set to 1 Doxygen will invoke dot +# for each graph separately, which can cause significant process creation +# overhead especially on systems with many CPU cores. Together with +# DOT_NUM_THREADS this setting can be used to optimise the dot processing speed +# for a particular system. Doxygen will try to give each thread a balanced batch +# of work. If the total number of graphs to process exceeds DOT_NUM_THREADS * +# DOT_BATCH_SIZE then additional batches will be created for dot to process. +# Minimum value: 1, maximum value: 1000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_BATCH_SIZE = 50 + # DOT_COMMON_ATTR is common attributes for nodes, edges and labels of # subgraphs. When you want a differently looking font in the dot files that -# doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# Doxygen generates you can specify fontname, fontcolor and fontsize attributes. # For details please see Node, # Edge and Graph Attributes specification You need to make sure dot is able # to find the font, which can be done by putting it in a standard location or by @@ -2525,20 +2731,24 @@ DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then Doxygen will # generate a graph for each documented class showing the direct and indirect # inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and # HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case # the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the # CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. # If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance -# relations will be shown as texts / links. +# relations will be shown as texts / links. Explicit enabling an inheritance +# graph or choosing a different representation for an inheritance graph of a +# specific class, can be accomplished by means of the command \inheritancegraph. +# Disabling an inheritance graph can be accomplished by means of the command +# \hideinheritancegraph. # Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. # The default value is: YES. CLASS_GRAPH = YES -# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# If the COLLABORATION_GRAPH tag is set to YES then Doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the # class with other documented classes. Explicit enabling a collaboration graph, @@ -2550,7 +2760,7 @@ CLASS_GRAPH = YES COLLABORATION_GRAPH = YES -# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# If the GROUP_GRAPHS tag is set to YES then Doxygen will generate a graph for # groups, showing the direct groups dependencies. Explicit enabling a group # dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means # of the command \groupgraph. Disabling a directory graph can be accomplished by @@ -2561,7 +2771,7 @@ COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# If the UML_LOOK tag is set to YES, Doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. @@ -2582,10 +2792,19 @@ UML_LOOK = NO UML_LIMIT_NUM_FIELDS = 10 -# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# If the UML_LOOK tag is enabled, field labels are shown along the edge between +# two class nodes. If there are many fields and many nodes the graph may become +# too cluttered. The UML_MAX_EDGE_LABELS threshold limits the number of items to +# make the size more manageable. Set this to 0 for no limit. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag UML_LOOK is set to YES. + +UML_MAX_EDGE_LABELS = 10 + +# If the DOT_UML_DETAILS tag is set to NO, Doxygen will show attributes and # methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS -# tag is set to YES, doxygen will add type and arguments for attributes and -# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# tag is set to YES, Doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, Doxygen # will not generate fields with class member information in the UML graphs. The # class diagrams will look similar to the default class diagrams but using UML # notation for the relationships. @@ -2597,8 +2816,8 @@ DOT_UML_DETAILS = NO # The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters # to display on a single line. If the actual line length exceeds this threshold -# significantly it will wrapped across multiple lines. Some heuristics are apply -# to avoid ugly line breaks. +# significantly it will be wrapped across multiple lines. Some heuristics are +# applied to avoid ugly line breaks. # Minimum value: 0, maximum value: 1000, default value: 17. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2613,7 +2832,7 @@ DOT_WRAP_THRESHOLD = 17 TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to -# YES then doxygen will generate a graph for each documented file showing the +# YES then Doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented # files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, # can be accomplished by means of the command \includegraph. Disabling an @@ -2624,7 +2843,7 @@ TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are -# set to YES then doxygen will generate a graph for each documented file showing +# set to YES then Doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented # files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set # to NO, can be accomplished by means of the command \includedbygraph. Disabling @@ -2635,7 +2854,7 @@ INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES -# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# If the CALL_GRAPH tag is set to YES then Doxygen will generate a call # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. @@ -2647,7 +2866,7 @@ INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO -# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# If the CALLER_GRAPH tag is set to YES then Doxygen will generate a caller # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. @@ -2659,14 +2878,14 @@ CALL_GRAPH = NO CALLER_GRAPH = NO -# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# If the GRAPHICAL_HIERARCHY tag is set to YES then Doxygen will graphical # hierarchy of all classes instead of a textual one. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GRAPHICAL_HIERARCHY = YES -# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# If the DIRECTORY_GRAPH tag is set to YES then Doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the # files in the directories. Explicit enabling a directory graph, when @@ -2689,24 +2908,29 @@ DIR_GRAPH_MAX_DEPTH = 1 # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: # https://www.graphviz.org/)). -# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order -# to make the SVG files visible in IE 9+ (other browsers do not have this -# requirement). +# +# Note the formats svg:cairo and svg:cairo:cairo cannot be used in combination +# with INTERACTIVE_SVG (the INTERACTIVE_SVG will be set to NO). # Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, -# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and -# png:gdiplus:gdiplus. +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus, +# png:gdiplus:gdiplus, svg:cairo, svg:cairo:cairo, svg:svg, svg:svg:core, +# gif:cairo, gif:cairo:gd, gif:cairo:gdiplus, gif:gdiplus, gif:gdiplus:gdiplus, +# gif:gd, gif:gd:gd, jpg:cairo, jpg:cairo:gd, jpg:cairo:gdiplus, jpg:gd, +# jpg:gd:gd, jpg:gdiplus and jpg:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. DOT_IMAGE_FORMAT = png -# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to -# enable generation of interactive SVG images that allow zooming and panning. +# If DOT_IMAGE_FORMAT is set to svg or svg:svg or svg:svg:core, then this option +# can be set to YES to enable generation of interactive SVG images that allow +# zooming and panning. # # Note that this requires a modern browser other than Internet Explorer. Tested # and working are Firefox, Chrome, Safari, and Opera. -# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make -# the SVG files visible. Older versions of IE do not have SVG support. +# +# Note This option will be automatically disabled when DOT_IMAGE_FORMAT is set +# to svg:cairo or svg:cairo:cairo. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2725,7 +2949,7 @@ DOT_PATH = DOTFILE_DIRS = -# You can include diagrams made with dia in doxygen documentation. Doxygen will +# You can include diagrams made with dia in Doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. # If left empty dia is assumed to be found in the default search path. @@ -2738,7 +2962,7 @@ DIA_PATH = DIAFILE_DIRS = -# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# When using PlantUML, the PLANTUML_JAR_PATH tag should be used to specify the # path where java can find the plantuml.jar file or to the filename of jar file # to be used. If left blank, it is assumed PlantUML is not used or called during # a preprocessing step. Doxygen will generate a warning when it encounters a @@ -2746,20 +2970,78 @@ DIAFILE_DIRS = PLANTUML_JAR_PATH = -# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a -# configuration file for plantuml. +# When using PlantUML, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for PlantUML. PLANTUML_CFG_FILE = -# When using plantuml, the specified paths are searched for files specified by -# the !include statement in a plantuml block. +# When using PlantUML, the specified paths are searched for files specified by +# the !include statement in a PlantUML block. PLANTUML_INCLUDE_PATH = +# The PLANTUMLFILE_DIRS tag can be used to specify one or more directories that +# contain PlantUml files that are included in the documentation (see the +# \plantumlfile command). + +PLANTUMLFILE_DIRS = + +# When using Mermaid diagrams with CLI rendering, the MERMAID_PATH tag should be +# used to specify the directory where the mmdc (Mermaid CLI) executable can be +# found. If left blank, CLI-based rendering is disabled. For HTML output, +# client-side rendering via JavaScript is used by default and does not require +# mmdc. For LaTeX/PDF output, mmdc is required to pre-generate images. Doxygen +# will generate a warning when CLI rendering is needed but mmdc is not +# available. + +MERMAID_PATH = + +# When using Mermaid diagrams, the MERMAID_CONFIG_FILE tag can be used to +# specify a JSON configuration file for the Mermaid CLI tool (mmdc). This file +# can contain theme settings and other Mermaid configuration options. + +MERMAID_CONFIG_FILE = + +# The MERMAID_RENDER_MODE tag selects how Mermaid diagrams are rendered. +# Possible values are: AUTO (use client-side rendering for HTML and mmdc for +# LaTeX/PDF and other formats. If MERMAID_PATH is not set, non-HTML diagrams +# will produce a warning), CLI (use the mmdc tool to pre-generate images +# (requires Node.js and mermaid-js/mermaid-cli). Works for all output formats) +# and CLIENT_SIDE (embed mermaid.js in HTML output for client-side rendering. +# Does not require mmdc but only works for HTML output). +# The default value is: AUTO. + +MERMAID_RENDER_MODE = AUTO + +# The MERMAID_JS_URL tag specifies the URL to load mermaid.js from when using +# client-side rendering (MERMAID_RENDER_MODE is CLIENT_SIDE or AUTO). The +# default points to the latest Mermaid v11 release on the jsDelivr CDN. +# +# The default CDN URL requires internet access when viewing the generated +# documentation. For offline use, download mermaid.esm.min.mjs and set this to a +# relative path, or use MERMAID_RENDER_MODE=CLI to pre-generate images instead. +# Examples: +# - Latest v11 (default): +# 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs' +# - Pinned version: +# 'https://cdn.jsdelivr.net/npm/mermaid@11.3.0/dist/mermaid.esm.min.mjs' +# - Local copy: './mermaid.esm.min.mjs' (user must place file in HTML output +# directory) +# The default value is: +# https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs. + +MERMAID_JS_URL = https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs + +# The MERMAIDFILE_DIRS tag can be used to specify one or more directories that +# contain Mermaid files that are included in the documentation (see the +# \mermaidfile command). + +MERMAIDFILE_DIRS = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes -# larger than this value, doxygen will truncate the graph, which is visualized -# by representing a node as a red box. Note that doxygen if the number of direct +# larger than this value, Doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that if the number of direct # children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that # the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. @@ -2780,26 +3062,17 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) support -# this, this feature is disabled by default. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# If the GENERATE_LEGEND tag is set to YES Doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. -# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# Note: This tag requires that UML_LOOK isn't set, i.e. the Doxygen internal # graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate +# If the DOT_CLEANUP tag is set to YES, Doxygen will remove the intermediate # files that are used to generate the various graphs. # # Note: This setting is not only used for dot files but also for msc temporary @@ -2808,11 +3081,11 @@ GENERATE_LEGEND = YES DOT_CLEANUP = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will +# You can define message sequence charts within Doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then Doxygen will # use a built-in version of mscgen tool to produce the charts. Alternatively, # the MSCGEN_TOOL tag can also specify the name an external tool. For instance, -# specifying prog as the value, doxygen will call the tool as prog -T +# specifying prog as the value, Doxygen will call the tool as prog -T # -o . The external tool should support # output file formats "png", "eps", "svg", and "ismap". diff --git a/inkcpp/include/list.h b/inkcpp/include/list.h index d7fecad1..45b93c2f 100644 --- a/inkcpp/include/list.h +++ b/inkcpp/include/list.h @@ -48,6 +48,7 @@ class list_interface { } + /** copy assigment operator. */ virtual list_interface& operator=(const list_interface&) = default; virtual ~list_interface() {} @@ -81,6 +82,7 @@ class list_interface } public: + /** copy constructor. */ iterator(const iterator&) = default; /** contains flag data */ @@ -204,6 +206,7 @@ class list_interface /** @private */ internal::list_table* _list_table; + /** @private */ int _list; }; diff --git a/inkcpp/include/runner.h b/inkcpp/include/runner.h index 59e3ece5..743d59a9 100644 --- a/inkcpp/include/runner.h +++ b/inkcpp/include/runner.h @@ -36,9 +36,9 @@ class choice; class runner_interface { public: - virtual ~runner_interface(){}; + virtual ~runner_interface() {}; - // String type to simplify interfaces working with strings +/** String type to simplify interfaces working with strings */ #ifdef INK_ENABLE_STL using line_type = std::string; #elif defined(INK_ENABLE_UNREAL) @@ -74,10 +74,8 @@ class runner_interface * * @param path path to search and move execution to * @return If the path was found - */ - bool move_to(const char* path) { - return move_to(ink::hash_string(path)); - } + */ + bool move_to(const char* path) { return move_to(ink::hash_string(path)); } /** * Can the runner continue? @@ -213,14 +211,14 @@ class runner_interface * Check if the there are global tags. * * @return ture if there are global tags. - * @info global tags are also assoziated to the first line in the knot/stitch + * @note global tags are also assoziated to the first line in the knot/stitch * @sa num_global_tags get_global_tags has_tags has_knot_tags */ virtual bool has_global_tags() const = 0; /** * Get Number of global tags. - * @info global tags are also assoziated to the first line in the knot/stitch + * @note global tags are also assoziated to the first line in the knot/stitch * @sa has_global_tags get_global_tags num_knot_tags num_tags * @return the number of tags at the top of the document. */ @@ -243,7 +241,7 @@ class runner_interface /** * Check if there are knot/stitch tags. * - * @info knot/stitch tags are also assoziated to the first line in the knot/stitch + * @note knot/stitch tags are also assoziated to the first line in the knot/stitch * @return true if there are knot/stitch tags. * @sa num_knot_tags get_knot_tag has_global_tags has_tags */ @@ -251,7 +249,7 @@ class runner_interface /** * Get Number of knot/stitch tags. - * @info knot/stitch tags are also assoziated to the first line in the knot/stitch + * @note knot/stitch tags are also assoziated to the first line in the knot/stitch * @return number of tags at the top of a knot/stitch * @sa has_knot_tags get_knot_tag num_global_tags num_tags */ diff --git a/inkcpp/include/snapshot.h b/inkcpp/include/snapshot.h index 5b8879f9..ad5b7b18 100644 --- a/inkcpp/include/snapshot.h +++ b/inkcpp/include/snapshot.h @@ -11,7 +11,7 @@ namespace ink::runtime { /** - * Container for an InkCPP runtime snapshot. + * Container for an InkCPP runtime snapshot, which can be @ref snapshot_migration "migrated". * Each snapshot contains a @ref ink::runtime::globals_interface "globals store" * and all associated @ref ink::runtime::runner_interface "runners/threads" * For convinience there exist @ref ink::runtime::globals_interface::create_snapshot() and @@ -23,7 +23,10 @@ namespace ink::runtime * ink::runtime::snapshot::can_be_migrated() "@c can_be_migrated()". * A not migrated snapshot contiouse at exactly the place you are currently at. * - * **A migrated one will "snap bag" to the last knot.** + * @section snapshot_migration Migration + * + * Migrating a snapshot will "snap bag" to the last Knot, there for it is best practive to do this + * only directly after choosing a choice. * * + Global variables which (name) still exist will be transfared. * + New ones will be initelized with its default value @@ -55,7 +58,7 @@ namespace ink::runtime class snapshot { public: - virtual ~snapshot(){}; + virtual ~snapshot() {}; /** Construct snapshot from blob. * Memory must be kept valid until the snapshot is deconstructed. @@ -73,7 +76,8 @@ class snapshot virtual size_t get_data_len() const = 0; /** number of runners which are stored inside this snapshot */ virtual size_t num_runners() const = 0; - /** if this snapshot can be migrated, if the story file changes (slightly). */ + /** if this snapshot can be migrated, if the story file changes (slightly), for details see @ref + * snapshot_migration. */ virtual bool can_be_migrated() const = 0; #ifdef INK_ENABLE_STL diff --git a/inkcpp/include/story.h b/inkcpp/include/story.h index bc3fba71..68862a27 100644 --- a/inkcpp/include/story.h +++ b/inkcpp/include/story.h @@ -25,7 +25,7 @@ namespace ink::runtime class story { public: - virtual ~story(){}; + virtual ~story() {}; #pragma region Interface Methods /** * Creates a new global store @@ -108,6 +108,7 @@ class story } // namespace ink::runtime /** @namespace ink + * @ingroup cpp * Namespace contaning all modules and classes from InkCPP * * (Unreal Blueprint Classes Excluded, but there will not be there in a normal build) diff --git a/inkcpp/include/types.h b/inkcpp/include/types.h index c771cb1a..e6cca05c 100644 --- a/inkcpp/include/types.h +++ b/inkcpp/include/types.h @@ -140,45 +140,51 @@ struct value { } }; -/** access #value::Type::Bool value */ +/** access a @ref ink::runtime::value::Type::Bool value */ template<> inline const auto& value::get() const { + inkAssert(type == value::Type::Bool, "Expected type bool, if a boolean should be readed"); return v_bool; } -/** access #value::Type::Uint32 value */ +/** access a @ref ink::runtime::value::Type::Uint32 value */ template<> inline const auto& value::get() const { + inkAssert(type == value::Type::Uint32, "Expected type uint32, if a boolean should be readed"); return v_uint32; } -/** access #value::Type::Int32 value */ +/** access @ref ink::runtime::value::Type::Int32 value */ template<> inline const auto& value::get() const { + inkAssert(type == value::Type::Int32, "Expected type int32, if a boolean should be readed"); return v_int32; } -/** access #value::Type::String value */ +/** access @ref ink::runtime::value::Type::String value */ template<> inline const auto& value::get() const { + inkAssert(type == value::Type::String, "Expected type string, if a boolean should be readed"); return v_string; } -/** access #value::Type::Float value */ +/** access @ref ink::runtime::value::Type::Float value */ template<> inline const auto& value::get() const { + inkAssert(type == value::Type::Float, "Expected type float, if a boolean should be readed"); return v_float; } -/** access #value::Type::List value */ +/** access @ref ink::runtime::value::Type::List value */ template<> inline const auto& value::get() const { + inkAssert(type == value::Type::List, "Expected type list, if a boolean should be readed"); return v_list; } } // namespace ink::runtime diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index 43325951..d4236983 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -36,6 +36,13 @@ void list_table::copy_lists(const data_t* src, data_t* dst) } } +inline list_flag read_list_flag(const char*& ptr) +{ + list_flag result = *reinterpret_cast(ptr); + ptr += sizeof(list_flag); + return result; +} + list_table::list_table(const char* data) : _valid{false} { diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h index d4d48605..62a8715e 100644 --- a/inkcpp/list_table.h +++ b/inkcpp/list_table.h @@ -9,7 +9,7 @@ #include "config.h" #include "system.h" #include "array.h" -#include "snapshot_impl.h" +#include "list.h" #ifdef INK_ENABLE_STL # include diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index d620c7eb..40096f36 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -170,8 +170,7 @@ void runner_impl::set_var( template inline T runner_impl::read(optional pos) { - using header = ink::internal::header; - ip_t ptr = pos.value_or(_ptr); + ip_t ptr = pos.value_or(_ptr); // Sanity inkAssert(ptr + sizeof(T) <= _story->end(), "Unexpected EOF in Ink execution"); @@ -345,7 +344,7 @@ void runner_impl::jump(ip_t dest, bool record_visits, bool track_knot_visit) const container_t dest_id = _story->find_container_for(dest_offset); // If there's no destination container, stop. - if (dest_id == ~0) + if (dest_id == ~0U) return; // Are we entering the new container at its start? @@ -486,13 +485,11 @@ runner_impl::runner_impl(const story_impl* data, globals global) , _choices() , _tags_begin(0, ~0) , _container(~0U) -#ifdef INK_ENABLE_CSTD - , _rng(static_cast(time(NULL))) -#else - , _rng() -#endif { +#ifdef INK_ENABLE_CSTD + _rng.srand(static_cast(time(NULL))); +#endif // register with globals _globals->add_runner(this); @@ -857,7 +854,8 @@ bool runner_impl::migrate_to(const loader& loader, hash_t path) _ptr = nullptr; jump(destination, false, true); - if (loader.old_ref_table && ! _globals->lists().migrate_variables( + if (loader.old_ref_table + && ! _globals->lists().migrate_variables( loader.list_old_new_map, loader.list_list_matches, loader.list_value_matches, *loader.old_ref_table, _stack )) { @@ -920,8 +918,7 @@ bool runner_impl::line_step() _entered_global = false; } else if (_entered_knot) { if (has_knot_tags()) { - clear_tags( - tags_clear_level::KEEP_GLOBAL_AND_UNKNOWN + clear_tags(tags_clear_level::KEEP_GLOBAL_AND_UNKNOWN ); // clear knot tags since whe are entering another knot } @@ -992,9 +989,8 @@ bool runner_impl::line_step() void runner_impl::step() { #ifdef INK_ENABLE_EXCEPTIONS - try + try { #endif - { inkAssert(_ptr != nullptr, "Can not step! Do not have a valid pointer"); // Load current command @@ -1456,11 +1452,9 @@ void runner_impl::step() // Load value from output stream // Push onto stack - _eval.push( - value{}.set( - _output.get_alloc(_globals->strings(), _globals->lists()) - ) - ); + _eval.push(value{}.set( + _output.get_alloc(_globals->strings(), _globals->lists()) + )); } break; // == Tag commands @@ -1606,11 +1600,9 @@ void runner_impl::step() read(); // Push the visit count for the current container to the top // is 0-indexed for some reason. idk why but this is what ink expects - _eval.push( - value{}.set( - static_cast(_globals->visits(_container.top()) - 1) - ) - ); + _eval.push(value{}.set( + static_cast(_globals->visits(_container.top()) - 1) + )); } break; case Command::TURN: { read(); @@ -1627,8 +1619,7 @@ void runner_impl::step() _eval.pop(); - _eval.push( - value{}.set(static_cast(_rng.rand(sequenceLength))) + _eval.push(value{}.set(static_cast(_rng.rand(sequenceLength))) ); } break; case Command::SEED: { @@ -1650,9 +1641,8 @@ void runner_impl::step() container_t container = read(); // Push the read count for the requested container index - _eval.push( - value{}.set(static_cast(_globals->visits(container))) - ); + _eval.push(value{}.set(static_cast(_globals->visits(container) + ))); } break; case Command::TAG: { read(); @@ -1667,9 +1657,8 @@ void runner_impl::step() *_debug_stream << std::endl; } #endif - } #ifdef INK_ENABLE_EXCEPTIONS - catch (...) { + } catch (...) { // Reset our whole state as it's probably corrupt reset(); throw; diff --git a/inkcpp_c/include/inkcpp.h b/inkcpp_c/include/inkcpp.h index d8de9579..4b1ebba6 100644 --- a/inkcpp_c/include/inkcpp.h +++ b/inkcpp_c/include/inkcpp.h @@ -302,7 +302,7 @@ typedef struct HInkSTory HInkStory; */ ink_hash_t ink_hash_string(const char* str); /** @memberof HInkRunner - * @copydoc ink::runtime::runner_interface::knot_tag() + * @copydoc ink::runtime::runner_interface::get_knot_tag() * @param self */ const char* ink_runner_knot_tag(const HInkRunner* self, int index); @@ -312,7 +312,7 @@ typedef struct HInkSTory HInkStory; */ int ink_runner_num_global_tags(const HInkRunner* self); /** @memberof HInkRunner - * @copydoc ink::runtime::runner_interface::global_tag() + * @copydoc ink::runtime::runner_interface::get_global_tag() * @param self */ const char* ink_runner_global_tag(const HInkRunner* self, int index); diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index a5119dcb..8523ecd2 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -229,7 +229,9 @@ void binary_emitter::emit_section(std::ostream& stream, const std::vector& void binary_emitter::emit_section(std::ostream& stream, const binary_stream& data) const { - inkAssert((stream.tellp() & (ink::internal::header::Alignment - 1)) == 0, "The stream is missaligned"); + inkAssert( + (stream.tellp() & (ink::internal::header::Alignment - 1)) == 0, "The stream is missaligned" + ); data.write_to(stream); close_section(stream); } @@ -275,9 +277,15 @@ void binary_emitter::output(std::ostream& out) header._strings.setup(offset, _strings.pos()); header._list_meta.setup(offset, _list_meta.pos()); header._lists.setup(offset, _lists.pos()); - header._containers.setup(offset, static_cast(container_data.size() * sizeof(container_data_t))); - header._container_map.setup(offset, static_cast(_container_map.size() * sizeof(container_map_t))); - header._container_hash.setup(offset, static_cast(container_hash.size() * sizeof(container_hash_t))); + header._containers.setup( + offset, static_cast(container_data.size() * sizeof(container_data_t)) + ); + header._container_map.setup( + offset, static_cast(_container_map.size() * sizeof(container_map_t)) + ); + header._container_hash.setup( + offset, static_cast(container_hash.size() * sizeof(container_hash_t)) + ); header._instructions.setup(offset, _instructions.pos()); // Write the header @@ -443,7 +451,7 @@ void binary_emitter::build_container_data( ) const { // Build data for this container - if (context->counter_index != ~0) { + if (context->counter_index != ~0U) { container_data_t& d = data[context->counter_index]; d._parent = parent; d._start_offset = context->offset; @@ -476,7 +484,7 @@ void binary_emitter::build_container_hash_map( const hash_t child_name_hash = hash_string(child_name.c_str()); // Store hash in the data. - if (child.second->counter_index != ~0) { + if (child.second->counter_index != ~0U) { data[child.second->counter_index]._hash = child_name_hash; } diff --git a/inkcpp_test/ListMatching.cpp b/inkcpp_test/ListMatching.cpp index 1d3cb19a..1a181870 100644 --- a/inkcpp_test/ListMatching.cpp +++ b/inkcpp_test/ListMatching.cpp @@ -178,7 +178,6 @@ SCENARIO("find best assigments", "[list_match][hungarian]") SCENARIO("Simple List Migration stories", "[list_match]") { - _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF); GIVEN("Splitted List") { std::unique_ptr ink_a{story::from_file(INK_TEST_RESOURCE_DIR "ListMatchStoryA.bin")}; @@ -196,11 +195,11 @@ SCENARIO("Simple List Migration stories", "[list_match]") thread_a->getall() == "More\nYou are still at Flor, Balcony - all posibilities are Flor, Balcony, Kitchen, Garden\n" ); - + auto globals_b = ink_b->new_globals_from_snapshot(*snap); - auto thread_b = ink_b->new_runner_from_snapshot(*snap, globals_b); + auto thread_b = ink_b->new_runner_from_snapshot(*snap, globals_b); CHECK( thread_b->getall() diff --git a/shared/public/config.h b/shared/public/config.h index 76d58f42..504b917f 100644 --- a/shared/public/config.h +++ b/shared/public/config.h @@ -32,77 +32,97 @@ // Only turn on if you have json.hpp and you want to use it with the compiler // #define INK_EXPOSE_JSON - +/** + * set limitations which are required to minimize heap allocations. + * if required you can set them to -x then, the system will use dynamic + * allocation for this type, with an initial size of x. + */ namespace ink::config { -/// set limitations which are required to minimize heap allocations. -/// if required you can set them to -x then, the system will use dynamic -/// allocation for this type, with an initial size of x. -static constexpr int limitGlobalVariables = -50; -static constexpr int limitGlobalVariableObservers = -10; -static constexpr int limitThreadDepth = -10; -static constexpr int limitEvalStackDepth = -20; -static constexpr int limitContainerDepth = -20; +/** amount of global variables in the script. */ +constexpr int limitGlobalVariables = -50; +/** amount of simustanly registerd variable observers. */ +constexpr int limitGlobalVariableObservers = -10; +/** maximum amount of tunnel/choice inception. */ +constexpr int limitThreadDepth = -10; +/** maximum size of the evaluation stack. + * Each operation inside an expression needs at least 3 slots. + * Also string building for choices with @c [] syntax will use the stack. + */ +constexpr int limitEvalStackDepth = -20; +/** maximum number of cascaded nodes. + * beside stitches and knots, choices are also containers. + */ +constexpr int limitContainerDepth = -20; /** number of lists which can be accessed with get_var * before the story must continue * @attention list vars are only valid until the story continuous! */ -static constexpr int limitEditableLists = -5; -/// number of simultaneous active tags -static constexpr int limitActiveTags = -10; -// temporary variables and call stack; - -static constexpr int limitRuntimeStack = -20; -// references and call stack -static constexpr int limitReferenceStack = -20; -// max number of elements in one output (a string is one element) -static constexpr int limitOutputSize = -100; -// maximum number of text fragments between choices -static constexpr int limitStringTable = -100; -// max number of choices per choice -static constexpr int maxChoices = -10; -// max number of list types, and there total amount of flags -static constexpr int maxListTypes = -20; -static constexpr int maxFlags = -200; -// number of max initialized lists -static constexpr int maxLists = -50; -// max number of arguments for external functions (dynamic not possible) -static constexpr int maxArrayCallArity = 10; +constexpr int limitEditableLists = -5; +/** number of simultaneous active tags. */ +constexpr int limitActiveTags = -10; +/** temporary variables and call stack. */ +constexpr int limitRuntimeStack = -20; +/** references and call stack. */ +constexpr int limitReferenceStack = -20; +/** max number of elements in one output (a string is one element). */ +constexpr int limitOutputSize = -100; +/** maximum number of text fragments between choices. */ +constexpr int limitStringTable = -100; +/** max number of choices per choice. */ +constexpr int maxChoices = -10; +/** max number of list types, and there total amount of flags. */ +constexpr int maxListTypes = -20; +/** maximum number of defined list flags. */ +constexpr int maxFlags = -200; +/** number of max initialized lists. */ +constexpr int maxLists = -50; +/** max number of arguments for external functions (dynamic not possible). */ +constexpr int maxArrayCallArity = 10; +/** Staistiac data for different game elements. + * use this to set you config settings appropriate to your scenario or just to get some insight. + */ namespace statistics { + /** Statistic data for an container data type. */ struct container { - int capacity; - int size; + int capacity; /**< current capacity of the container. This does typicall only inceares over the + runtime. */ + int size; /**< current size aka activly used elements inside the container. */ }; + /** Statistics for managed lists, including static and dynamic enties. */ struct list_table { - container editable_lists; /** based on @ref limitEditableLists */ - container list_types; /** based on @ref maxListTypes */ - container flags; /** based on @ref maxFlags */ - container lists; /** based on @ref maxLists */ + container editable_lists; /**< based on @ref ink::config::limitEditableLists */ + container list_types; /**< based on @ref ink::config::maxListTypes */ + container flags; /**< based on @ref ink::config::maxFlags */ + container lists; /**< based on @ref ink::config::maxLists */ }; + /** Statistiacs to managed strings, which are build at runtime. */ struct string_table { - container string_refs; /** based on @ref limitStringTable */ + container string_refs; /**< based on @ref limitStringTable */ }; + /** Stastics for state managed for one runtime. */ struct global { - container variables; /** based on @ref limitGlobalVariables */ - container variables_observers; /** based on @ref limitGlobalVariableObservers */ - list_table lists; - string_table strings; + container variables; /**< based on @ref ink::config::limitGlobalVariables */ + container variables_observers; /**< based on @ref ink::config::limitGlobalVariableObservers */ + list_table lists; /**< Staistics for all lists associated with this runtime. */ + string_table strings; /**< Staistics for all strings associtade with this runtime. */ }; + /** Stastics for state managed for one thread inside a runtime. */ struct runner { - container threads; /** based on @ref limitThreadDepth */ - container evaluation_stack; /** based on @ref limitEvalStackDepth */ - container container_stack; /** based on @ref limitContainerDepth */ - container active_tags; /** based on @ref limitActiveTags */ - container runtime_stack; /** based on @ref limitContainerDepth */ - container runtime_ref_stack; /** based on @ref limitReferenceStack */ - container output; /** based on @ref limitOutputSize */ - container choices; /** based on @ref limitContainerDepth */ + container threads; /**< based on @ref ink::config::limitThreadDepth */ + container evaluation_stack; /**< based on @ref ink::config::limitEvalStackDepth */ + container container_stack; /**< based on @ref ink::config::limitContainerDepth */ + container active_tags; /**< based on @ref ink::config::limitActiveTags */ + container runtime_stack; /**< based on @ref ink::config::limitContainerDepth */ + container runtime_ref_stack; /**< based on @ref ink::config::limitReferenceStack */ + container output; /**< based on @ref ink::config::limitOutputSize */ + container choices; /**< based on @ref ink::config::limitContainerDepth */ }; } // namespace statistics } // namespace ink::config diff --git a/shared/public/system.h b/shared/public/system.h index b3542147..dc8f1eb9 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -40,13 +40,26 @@ # define FORMAT_STRING_STR "%s" #endif +/** + * @def inkAssert(condition, format_string, args...) + * @ingroup cpp + * Compile argument agnostic assert macro. + * behaves diffrent base on if it is an UnrealEngine compilation or standalone. + * Also respects INKCPP_NO_RTTI, INKCPP_NO_STD and INKCPP_NO_EXCEPTIONS. + */ +/** + * @def inkFail(format_string, args...) + * @ingroup cpp + * Compile argument agnostic assert macro (always asserts). + * @sa inkAssert + */ + #ifdef INK_ENABLE_UNREAL # define inkAssert(condition, text, ...) checkf(condition, TEXT(text), ##__VA_ARGS__) # define inkFail(text, ...) checkf(false, TEXT(text), ##__VA_ARGS__) #else -# define inkAssert ink::ink_assert -# define inkFail(...) ink::ink_assert(false, __VA_ARGS__) - +# define inkAssert(...) ink::ink_assert(__VA_ARGS__) +# define inkFail(...) ink::ink_assert(false, __VA_ARGS__) #endif @@ -115,13 +128,6 @@ struct list_flag { bool operator!=(const list_flag& o) const { return ! (*this == o); } }; -inline list_flag read_list_flag(const char*& ptr) -{ - list_flag result = *reinterpret_cast(ptr); - ptr += sizeof(list_flag); - return result; -} - /** value of an unset list_flag */ constexpr list_flag null_flag{-1, -1}; /** value representing an empty list */ @@ -238,7 +244,8 @@ class ink_exception // dependend on rtti, exception and stl support not all arguments are needed # pragma warning(disable : 4100) #endif -// assert +/** Assert helper, not to be used directly, please use @ref inkAssert and @ref inkFail to be + * enviroment agnostic. */ template void ink_assert(bool condition, const char* msg = nullptr, Args... args) { @@ -273,6 +280,8 @@ void ink_assert(bool condition, const char* msg = nullptr, Args... args) # pragma warning(pop) #endif +/** Assert helper, not to be used directly, please use @ref inkAssert and @ref inkFail to be + * enviroment agnostic. */ template [[noreturn]] inline void ink_assert(const char* msg = nullptr, Args... args) { diff --git a/unreal/blueprint_filter.js b/unreal/blueprint_filter.js index 39b17c1f..63457ff8 100644 --- a/unreal/blueprint_filter.js +++ b/unreal/blueprint_filter.js @@ -273,10 +273,32 @@ Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="" End Object ` } -function construct_ueasset(type, signature) { +function build_actionbase(type, name, args) { + for (const key in args) { + args[key] = build_pin(args[key]) + } + return ` +Begin Object Class=/Script/BlueprintGraph.K2Node_AsyncAction Name="" + ProxyFactoryFunctionName="${name}" + NodePosX=0 + NodePosY=0 + CustomProperties Pin (PinName="execute",PinType.PinCategory="exec",) + CustomProperties Pin (PinName="then",Direction="EGPD_Output",PinType.PinCategory="exec",) + CustomProperties Pin (PinName="Completed",PinFriendlyName=NSLOCTEXT("UObjectDisplayNames", "", "Completed"),Direction="EGPD_Output",PinType.PinCategory="exec",) + CustomProperties Pin (PinName="Snapshot",,Direction="EGPD_Output",PinType.PinCategory="struct",) + ${args.join("")} +End Object +` +} +function construct_ueasset(type, signature, doxy_blueprint_args) { var builder = undefined; switch (type) { - case "BlueprintCallable": builder = build_call; break; + case "BlueprintCallable": + if (doxy_blueprint_args.length >= 2 && doxy_blueprint_args[0] == "ActionBase") { + builder = build_actionbase; + } else { + builder = build_call; + } break; case "BlueprintImplementableEvent": builder = build_event; break; case "BlueprintPure": builder = build_pure; break; default: throw new Exception(`unknown type: '${type}'`); @@ -345,15 +367,16 @@ for (const arg of argv) { var input = fs.readFileSync(arg).toString(); var out_str = input; var offset = 0; - var re = /(DOC_UF|UFUNCTION)\(\s*(?[^,]*),[^]*?\).*\s*\/\*\*[^]*?(?@blueprint)[^]*?\*\/\s*(?[^;]*);/gmd; + var re = /(DOC_UF|UFUNCTION)\(\s*(?[^,]*),[^]*?\).*\s*\/\*\*[^]*?(?@blueprint(\{(?[^}]+)\})?)[^]*?\*\/\s*(?[^;]*);/gmd; while ((m = re.exec(input)) != null) { let type = m.groups.type; let signature = m.groups.signature; let pos = m.indices.groups.pos; + let blueprintParams = (!!m.groups.blueprintParams && m.groups.blueprintParams.split(",").map(item => item.trim())) || []; let output = new HTMLElement("body"); let document = new Document(); new window.blueprintUE.render.Main( - construct_ueasset(type, signature), + construct_ueasset(type, signature, blueprintParams), output, { height: "643px" } @@ -374,4 +397,3 @@ new window.blueprintUE.render.Main( } ).start(); console.log(`${prefix}${output.querySelector('.node')}${suffix}`); - diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h b/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h index 681e5a6a..3207ff66 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h @@ -15,16 +15,21 @@ * @ingroup unreal */ USTRUCT(BlueprintType) -struct INKCPP_API FInkSnapshot -{ + +struct INKCPP_API FInkSnapshot { GENERATED_BODY() - FInkSnapshot() : Migratable(false) {} + + FInkSnapshot() + : Migratable(false) + { + } /** @private */ FInkSnapshot(const char* snap_data, size_t snap_len, bool migratable) - : data(reinterpret_cast(snap_data), snap_len), - Migratable(migratable) - {} + : data(reinterpret_cast(snap_data), snap_len) + , Migratable(migratable) + { + } UPROPERTY(BlueprintReadWrite, SaveGame, Category = "ink|SaveGame") /** Raw data used to restore runtime state. * not needed if a USaveGame is used. @@ -37,32 +42,43 @@ struct INKCPP_API FInkSnapshot bool Migratable; }; - DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( - FInkMigratableSnapshotCompleted, - const FInkSnapshot&, Snapshot + FInkMigratableSnapshotCompleted, const FInkSnapshot&, Snapshot ); +/** A helper class to create migratable snapshots. + * creating an instance with ::GetMigratableSnapshot() will @ref UInkThread::Yield() "yield" each + * assoziated thread after the next choice until a migratable snapshot can be cerated. all threads + * will then be @ref UInkThread::Resume() "resumed". + * @attention if a thread is inside a tunnel it will still yield after a choice and will then stop + * at an point where it cannot create a valid migratable snapshot, fix still pending. + * @ingroup unreal + */ UCLASS(BlueprintType) + class INKCPP_API UInkMigratableSnapshotAsync : public UBlueprintAsyncActionBase { - GENERATED_BODY() + GENERATED_BODY() public: + /** @private */ + UPROPERTY(BlueprintAssignable) + FInkMigratableSnapshotCompleted Completed; - UPROPERTY(BlueprintAssignable) - FInkMigratableSnapshotCompleted Completed; - - UFUNCTION(BlueprintCallable, - meta = (BlueprintInternalUseOnly = "true")) - static UInkMigratableSnapshotAsync* GetMigratableSnapshot( - AInkRuntime* Runtime); + UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true")) + /** Tries to create a migratable snapshot, on completion returns it. + * see @ref UInkMigratableSnapshotAsync for more details. + * + * @blueprint{ActionBase, Snapshot} + */ + static UInkMigratableSnapshotAsync* GetMigratableSnapshot(AInkRuntime* Runtime); - virtual void Activate() override; + /** @private */ + virtual void Activate() override; private: - UPROPERTY() - TObjectPtr Runtime; + UPROPERTY() + TObjectPtr Runtime; - void HandleResult(const FInkSnapshot& Snapshot); + void HandleResult(const FInkSnapshot& Snapshot); }; diff --git a/unreal/inkcpp/Source/inkcpp/Public/inkcpp.h b/unreal/inkcpp/Source/inkcpp/Public/inkcpp.h index ffb0f0c8..91701a51 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/inkcpp.h +++ b/unreal/inkcpp/Source/inkcpp/Public/inkcpp.h @@ -17,8 +17,9 @@ * ```sh * /UNREAL_ENGINE/Build/BatchFiles/RunUAT.bat BuildPlugin * -plugin=/AN_TEMP_DIRECTORY/inkcpp/inkcpp.uplugin -package=/YOUR_UNREAL_PROJECT/Plugins/inkcpp - * -TargetPlatforms=Win64` - * ```
And either way activating the plugin. + * -TargetPlatforms=Win64 + * ``` + *
And either way activating the plugin. * * The C++ API is available — include the plugin headers directly from your game module * after adding `"inkcpp"` to your module's `PublicDependencyModuleNames` in Build.cs. @@ -185,39 +186,57 @@ * width="80%"/> * @endhtmlonly * + * @htmlonly * * A InkThread Yield Resume example Blueprint + * @endhtmlonly + * + * @htmlonly + * + * A InkThread Yield Resume example Blueprint + * @endhtmlonly * * @subsubsection ue_example_demo_DemoThread DemoThread * + * @htmlonly * * Example of the ussage of TagList::GetValue inside processing a new context line. + * @endhtmlonly * + * @htmlonly * * Example for choice handling. + * @endhtmlonly * * @subsection ue_example_minimal Minimal * + * @htmlonly * * Minmal InkRuntime Blueprint + * @endhtmlonly * + * @htmlonly * * Minimal InkThread Blueprint + * @endhtmlonly * */ From 98512b2407a4478e2ca3e924af00cac9615df7b0 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 4 Jun 2026 20:42:24 +0200 Subject: [PATCH 15/29] fix(python): add dropped argument lookaheadSafe for external function binds --- inkcpp_python/src/module.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/inkcpp_python/src/module.cpp b/inkcpp_python/src/module.cpp index 5706d00b..e6403f33 100644 --- a/inkcpp_python/src/module.cpp +++ b/inkcpp_python/src/module.cpp @@ -453,10 +453,14 @@ iter(inkcpp_py.Runner) returns a iterator over all current choices.)", "bind", [](runner& self, const char* function_name, std::function)> f, bool lookaheadSafe) { - self.bind(function_name, [f](size_t len, const value* vals) { - std::vector args(vals, vals + len); - return f(args); - }); + self.bind( + function_name, + [f](size_t len, const value* vals) { + std::vector args(vals, vals + len); + return f(args); + }, + lookaheadSafe + ); }, py::arg("function_name").none(false), py::arg("function").none(false), py::arg_v("lookaheadSafe", false).none(false), "Bind a function with return value" From 7b546c7285a0361fb4378c72fcba07a20dd1a00a Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 4 Jun 2026 20:45:12 +0200 Subject: [PATCH 16/29] docs(UE): update UE demo for the new features (e.g. migratable snapshots) --- .gitignore | 5 +++++ Documentation/unreal/InkCPP_DEMO.zip | Bin 227917 -> 303895 bytes 2 files changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index b433ae47..4a347881 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,11 @@ build/* bin/ Bin/ +# python build artifacts +dist/ +__pycache__/ +inkcpp_py.egg-info/ + # Visual Studio /out/build/ .vs diff --git a/Documentation/unreal/InkCPP_DEMO.zip b/Documentation/unreal/InkCPP_DEMO.zip index 6ae55b556a6a460bb5c9940e7c3dbc85db277308..8972ed105ef3b36d61a9eb35a27325f69b2f34a0 100644 GIT binary patch delta 213781 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

o!?kVxFo&#!Ll*OfmqqKuW(*ZMzj1rDGR> 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 f`*b-{Pa7YXXmg@P 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%0D=9uQDwWob;-r%x@>DQyxw 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 delta 137672 zcmZ^}1yCGM^e&18f+vs!_uv{_f+Rq22oebH?(REi2*KT1EJ$#7cXxMp7T0C>@w>PF zRrkJEZ>pxIr@K#|@0|0Uo}Qjb>cbno#>V+Bhm7(L;lINw);J3uCm-SexAir`YXqs-I1)tGm>DuGjTfo^`uumsJd+{**C6}9YX4`4KGXGa~wFh2)0NmXg@$CeQSLl>cvo{x{^8aT?!#0sce$Kh39_*x%nteXx7;{w<1z1a%H| z-*1*-YEcYSL(LCAkprkRd${;HXykvI@p4r&sD~3Oe7$DU$Rvh9sWOu&#(Um{>IKO{ zi*<~gc4_G{C$I2~h{jcOLxGE#WQW!2)#mEz>f^__;-+Gk*-I}y;Q8_9_zuv6SJ)pm z+*%6@3p;xwzWIRFAt|kl9@0*rt~0%wzGZ<4f7)iJKOIU*#_-gT#lf*>;K^(I>3O~w zU#PE-70|*?3YYiFI>7Bu6OCB%xOMj35*tVp+5hPp>e^!W~;x3u>AqQXTHHr0<3?EH*JlLGd#HESJMPA%A{aZ({IYI4Ll4aC&Qxo7>N-i zRKtqs=xU=azha;Ua^7p;Dg@>??h@aHW6qOgL=IG&i0OKS#1uzL{~OLmZ$nAzUNZMG>qYsf`Rq^ zfm#hizF;W9A2Jr~KlKqz$4dNv)mV*@%dy^CO~}g!h}EjSZUHjh=6x_Yz5gRwWvze{ zT3(}i)yS#-PP2?0M@3rZ-Gr{(pg4}2cGnblw?943&+e@Wqh-=?T?2tZ7LNFy3A47r zF=s6L(2nm74BQy5DMQk9sUHZH^Q>&K27LJHX}|4Vsk~)Rwq*%Q!b}LC7#?E85C#j%t-crsL%NagFNSV6~9T9rVLz|~X!-&J3$Km(6cP0ZW`6-p}`G1l`K*wbR*AFSu z{?cGul)33em#lmySDSO&r4eP=wmjGX??K(jo!DJHl5OJ|0?J06A8{tFb{joygnt&d zmxH3eU5#)Xc%O~(b0-uzM06zRK_n^J8OnX*i=3J5AA$bc>WgUIFS9%*`aR#A`u0P{wj=(hhx`q4>K$-FEODW$DqfoH<0mr(bux6?R_oSc~y~qD){Gn z0`#*?rs^wN9>1IA1&WD*OQULY0?blem|~ZMi|p6IrCdZiJR7@)RNQzMn0ghH3E>~V zNJ_l9Jh^)6&t@^(JkpHp%){#Y5KEswbYSHF7ag+)h~`zRrH39m4)6JsmmK0m1}+AA z+DQg9XAh}#vt)fgM{v9S#-_<^LfFKYUuO0%qT%nq%X z$;+0k&_KhHL&x?5@qzf4ZzMKtZv}l$0Ev1qu;NN?8i!_s@tB&9C~@V97@~( zO9o*(;y!i;8$Z(Y>{@Jae#!_s6!^{j8X=_bl!B7&vzQLZui-7?)*s}a1eD)tc$8y= z2xlxkDaaie{tYVFD=6~Y9KyJc>e!?g$oTo|UsuFIeXMHdQ%F63(S8rVev?E*$;K>3 z`Wkq{r;IGsM=273Gt@}j0HFfUi2HGgOj42mEy|;$ry#44LGKdXC-@GK2c0-$%$Ki0^hFr8F4>E=>fbfYC!IjRQOb81fR2dP zut+lXQ|{Fe?)kiwxMDIYB;c1k(9{s+Sax;n?l2mcyZS~0S=Kw#+%>S7Suj=OwGBRP z%|Ox!5v$ubJ%7%jv)SKlrbl;l+&Nd5gF^gTE~{hTzQRT-tDRJKSo^MG!xKdQ|J!R-BTt%n$kD`ialH8ncB+m{E`n9F()#eg*5M4K|Vd5V89 z1ou8p>9bN4(*1m32z;&5KQvsr|c)fYJ>nJeX6!1xuNR{)4Uzd6tD?lV1i6k4I zFEC?}!Ojtjw_kigeeD6pr|-vAL(25nN**2_BWC|_b|$SinK|kDL-<1auS+j&at{MY z%B>WCQg#mIMp?-x2;)G7LX#KfHY+;$>1lwEW}VAjy(ON)Ovi|daYD{3NH#$)nIP!c z!X?CKI5BNoRv3Y|pb7H$l&>KN%Lpu9eKOp-1_3R&#%qr*2{Qcnf2xJe)W4HPUiz zzNb0uch;we2^CEz_=28$t{B6j?@3p7IG>rWic#y;p0o~)o?^5N446^4R@MNvJACG! ztyi~0?M797A?O!J+m9*lXG*p_!y|Z&RW|57mA*%rJ6&mAz4I#Gi520ry~$-i%$-p_ zb}zsAF>q5?fa^QUt9D#a0)O|&w1Y=xx`grB<|Mm3k)qI3G`LOH8-W(pk5 zg^AF$q93xOg6{x{1m(`8M9RYVF-bmNeFB_I>aE(FTsUsD29}e~V(|3GO1}~$#J6T7 zs1GeU*xy}Wk3bYxQ2#@=NN$V-PB<{G!Q<^Q`l?Z%!{*zpyMsuW`2tCSW$opsg z(&%>dSI?ibwG+YgJH;!zLGjCZPMC@vGuCnv&AOsG^x8RkujYPhuJ6{ZleIaxnA;yE zX*6AZLmGhStw@)F+#Onbz2NTa>!;@HQ;w(B%%hzk#egLGCH%7e zoa?tfnuuFGWLU=j_Gv@yHf~$vtXYFXkJo&2I7A`!gtYdtViq^^YAlcp*<XL@K3kMFn28bDMj;7eHWD(bDVYb z?R1yN#lbW}qFQr2r#TZR^W|PP#SQWwARKAOeHkh}g zGvN7%E!wA3KK7$UdqA;@CuM-H-?OjzT*SEk4Dd~Px-`#p2iKmvv%-OAbl;T6ZhCLw z@B!tPHdZC>vy{m1mr&=UhmDHR69U_qd9<#Ay}k1)HEXUk*M&tP?5u#fv5(8AS;FLm zAKS)d_f-zvcp47uGHtqk!6tO)JCpuzslysbzAf}gqA4MNDO?_ zPEOZ;N^Glu^u~I{Y%fOq@^9R7T3qqsay@h`B_usT#{DB#BahAHDxhR&`Qe-4e4lBG zNlpq`^RSe_5=|UaaG_m{SuPQN+sD*Xm*#amvI(8WTZ_5=_Zm!T z^YjSE@wWfXpqKD7E+b8AX^foTVC9Q(7E8K?y*#__QffMQC_gbzOSR<_7lgPuxFN+Z z5ELYs#A2y%iD<3FTb;0dmtN;OsNmDYGiU~J-0=4X5}TVXoV-7#$mMBOSL2-ys`GLZ z=m;SB(bHCOHOHpN|0A+8?2h!XJc#@*;%VtE7?(bqI#8R(W$PTfs6#i4;yY9|J|-v9 zzJ7z8*s$8k&&D0#ucQuYF;^E1#HSkNohiz_gtp^F-3<;&m;aEx^qRr|xR)jIV zKJspjM~#1u$V^Tf8C7ziR?kd^L(_nD#4|-XHU;j|lomv}Z)Fsj-Ri(k_iYOj7460WTCUU3InTwNk=r?*lk55NjMfMY4tE9K zB%3twh>5?f;t=*qIfC_eYUD=}&dZ#6w}3r)C2o59I)`vs_I(GHQ`wp-T|G@G#m@=3 zsY7*Mki%6o$2k>@R%wmqamAOnB9ivfk`Vpl*juoU2TJ6xpxvw4l+nM-DpFUIy0^E$ zu&V5}5F5qp{$7+QXH8c_LuH@EVHrfvEg7oFm)JebV_BpK=DA-Q<-!|2tFofVw zt>{oEnZ&H0#RLR5o7m;h4yxNdKR!v*JI9r>rmHv&&I;7+*!Qn-y}?ZStAQxFc9#@| zi}xx+L1EA)F~e;&ukxOdV%39gbuq>Y&>vFtc!-PpCQbz&8Ixd!AnLy&J9IO6RiClx zOVr0u^NVkDl~2Tn5KZ2vaf3koP!ZIzmip<8prWw*Wb9%56!`}qt(JDu$5i!-D3KPM zW8KXU?0$wiJrAV%njGt4dmo)MQrz+wZ+D%w+pC@5l2%P171pznx31T{`reWB0qBiu zl#CN_t+1|Zx}9E?g(%6@v5&GccU<>DGTTht-^WIosZ!Tl*s*oP-}{k^R@$wJNM`A_ z;4vYrGCFG1 z4tlNo$?WFd;H(ay7a-he!>LW6Kze63sXY06K-_M70^8v8aMj?*MC4dQTZyw!<9`XGNL{D&FvSja$NT zD8h9}4XtxNOqMzC?VFdROO&BqPSc(r+0bS5xL##mdvqztk&^3!Y=_+e!ftoelpy-q z@On%!VCr(<=9TZeX>8IGgfj~E*0&3P5!jd|RS{`LZ*Q8s-rErCUz*9CwDsc=5WE?$ zamgW~V@s5&AGK8=_}UIC8gDMZ5Eiba7GUl5w92W?yW6*$uQ2}tX?&*>N8tP_OUZo> z{9P9;>6m@PX3(Wg`p_lDGDMq{-#Bs)T;I!O68?LKa9VZM z8iGTV{cda*gO&q2N#hRsAR*Jh(`owEZuSV$T~GIz=xal`CP&9P;LbJZ#_Q$*C;K%# zHF&D)QRwiK=#{X zFMJ?}2#W5uUNBaf^UtkJ$RD~2zfH)MFm71biD6Xeq6WQXuH~)ChmoNn%)kb4-lb8v0QiFQZhzZR+v5lO_UkA)jMRl#F@O#MbC?^4eVS=tWN_&z7j?t#=~6F)T?{^456d`bWi2u^ zKm%ehB=n4dLj6lwhj^#J;y*HqjtVloZVP#p&pjDB0lKYE;4{+j^~<@r*5!3LzeQ2Z zy8_1VsP3*6Gr7fJeA7at?FG{3!1}Mbl_e5871ob;aI`L^DE8FJ#kwHtgcGOnAPz#N zQKX#&W&n;o67NC1etyv?4u91!bhLYdNW$1hRGMQ6pQ*GGF9-v_|s)@&L+0C{_cF) z;JrknK#a_}vRL#J{A!Bpxzfe*54%6w5w(3Mp_C!&dvVU;SbFTD^|cd44t~bjl@Se? z+#T!7qO1;EZCcZ^%%Hj%hpfe0pXJEL_zG~<^5XD)OC8@T>L{Qn@~&44KYhLAxk>rM zMtDopw&^m~-vFrJ80heyxIy+et`zPevBc$~O-xb_$-&+jpWB90n2Nm<4PkoBSP^k$ zvC^4Q@*0CfiLPVLf;Jf9H1D8k7QH+=4?hKXN#|w<>D*45^=+ec-{~vLVr2`vdH*OQa_;EETfp;%hc`5-Q3}R z7&pb^@X)*+I9agc@H)qqf}|ZU#7Zi81Gar?&Qp)xS8UELxz8Ze$yCs zWTVU#wapNAy@ww^T|Eg-oc7Ro72L~;LNk()GRHZo3TaOU*lUoyp0^60`ocTY0#txJh2Mdo ziN`PcVM`7hd(g5&dCn;$o?KHr77srV`5Yzk_bcqFGck|;7D$M31@5Xgq# zaz#p2ucJsK=*&9|fse5L22z1r3GILAYFUz9i#5OV&ApnRQmXtoBJj?yOK%t5pPc*- zVlU@gpDrW!-gshq%@*E!FB5ZQVLB_X5AyJ3ANugS>_r)t70}I>$eO2CvwXJe^QH97 zcjONKN;UlJq^0+Y!@XmNWV%s;d35z)Gy43?z*8y{RX8c#Woqtco#5=;B5K}8fx#ss<`=4 zJz<@dz{YXj{7zKkn0>ugvY+UOv4chMvcPi72iMcb&4=cPPs#T~tq{Aw-W9epS+gCn zH}=BMor!Q;TPG5LfOFaE4j{$yHxt&;EUE&Va6{&`0?#c{Ec zWs64n`D;~}U|N+B;rb}Kr(@V&H5w7+O4a=;v-cTQ^vElF;^vJDxE>C(hgk?A?^v{< z@9xuQxBCMT{_g`%3qkn(^;l!TE9=bOv}HDmO!gt?ekxmlK!GBj9aRK=oDD3ZOIz&y zbF;(h;C7hZ7;^=l>CgK$L-XH<{vEVDq&sh<&C42f`^fJfHB>6#{2IyhvTmZTyi5p* zAq!dBZRgot2UXv}&b{F%9l*_H|HWbrGkU@|Wo+2kbNl$zwH2aS&3b@iQr3g%H>FjkYpk0TfhW(*sx4j&< zvmE#pvhi!hPWpOEcDTy+R}X=h5?97)oIsM5q9c;6Za3`k!Gz?1;!m^lndrmF+v~qX zyy<3jH6{X{6i+kl)DM^4(}#7Twug#qm6F=vyK6C~{>_!63-3oPRScAr%;(NavJmFK zJr1xsNZkCt-mc;9OxUd5li2kBF9P(;4m-fZ?I|g{-8n6hb{Ws+xqH7PpmNhnSSrqD z?D$`YC4Hvzwze5x3=7V^>oC%RO(F@3&E(B~cB-0m>BapPNr|UYJJp4A82E2L}f`3w)LS@>?{LZdRl+Q2W4*b)s0=@B{H=8ud zy0;=tRR7V=N_^grsZY$p2_t8`JrJ&6<&XfSC0p<8(Rv>e&c}6|q+DxOsy>}u35-ap zdvM)086Oc|7|@Ng;d-!CqxqkUR048G?7}V$2#0DYGRV!B#hnjEdB>`Xgdox}>FndM zQ^?}6q1Zz50L$(S|HaZlaNw1GueJfu*x+1eF-Umv_)P^K}L67{Ty=Puv}4uYT)IsI=)T#kDd&MCSk-hW!kP!&Co zVq!?#(>8b}fk)$Jy%G!?j$>H{-5u-x9Fdwn9;=5IM$%|kCgZB+yJCZtNJo^=R-1UP zo%DDu`ES|a)0=npG#FfT*MQ*jS}!3YLVfS8!@diATg)JULU(=R{;kh(j(z7i>V|MO zG~>tvDgnM{i$`Cgjtz)s*5T3~)U~7DEMI|~Ypkw{>fJ69n6dbR3NKme_Uq)L`i1A& zW-lL~nPKkV7^8okJxmJ3WY3TpMp7(~e_3}F+xAu>L6LJ6(T_U}2e`WScxrHh5U|Sn z9Wlm+%{keJm_)^umV7@S6k|@M%$XT!_Q*lfobAcNA%;aJFWWp$tG3u;ZXx3%=P9NU z+O73iI=J{jcjichFFQMHBdA2vgG9P+Ncu;oqdG%bw_sCIACJe@U}bEH{?50eFjyfJ z`+U)a{xvf zYZZ{d9ApMnUn;wzPh)J0s+$fz>|-Gmlxqs~Bw z6F#8}>n8P{BE4d7y6ZUwH;9``wN!R;h`c5H`Ln2q8<5vq{k{NQp|@oy!NOPv&J(u? z)H*0O3p9KHIlAl{fdB%=wQ@;Ax9wF--Q^;8!#^D~m}!ee`7F(wP+vem>s9hfTYxBb znIfHP_4AifOrz;6u1yPzvnfCJOQ$gDYcL8Xd3E z^ub#wA>eg?D@qgm^1bz_9|tNGZq0Z!YTS^Ropdx1x?SLQ@-N`ESu_|2ozM0}pAIr2 z>kA3nnj{L_Q%dLZG_uf&U-g6*#i`kQ=~3Ib`gUt=W@zjgtJl;qy?mZQDLV>3}n|F^%~&c2!N)Ez6Stq;K&NmUSgyRVBwo}-e5Ls-7xVqlam z@4IK9;t$y7qNsc&>bESP&0^=g?jywnc8+A~FuvW%E%xMgzlM&~Wy-Yq}x8<5>4jbW$)}#y#)jzZCD42GG024;4~+7sg7f&mtUp zmu8NNarI{9IOH6l8b`S^wXF`HrrW-C+tRh+J%c6U=?2=0?THn`CuVm6N0+^cMb1xY zfCAs&b7O|tu{7T5z+0?3fyaY5weVMV(tAJ-EO@fly%)pi0vX;Sdl;}KV^Xf*b!xPm zxHWEE9Nl}h^x(_BmuZA8B=#j&Lruu; z`ic~W8uk(xMM&00B$JX?kmkMj7GtPdehig{+_S@d)L@B2dD;;2HS=XC+2(g^VD~io z?oRk9r!x2I4%71v86bwJoV~9&K})6AH<~GQI*PZ4@^Hk;o})Bx-IeR1qF^82s{cjJEK6gO~5w^*0L@o`~thC@z8qejjn6SBi@@ zvxdV8`&ktklKGSzSBr4A#4pwb!1q?`nldjn@k_IK>0$nRn#4fYuwO=>4#>FIRr}Mu=$2A98^avJ<}bP^I99%+2OETVX}~0tU0(@q z=SD>?qT9xOEgMdQU5x5JMN#7WV{(-V@z6Nm_qS1u5z6n*JZD45=6O)w17>z?FB^E` zhqEWc@N`%|%rhg!_pSEb#&4DPsjcm`&U0fAmrg?p^N=!0-<;8&JC3BP?hG-bGKLGWwP}B346{!<<1IMq$-sIR zHhuY#N@-%hKIMXDv+DZJ1i*3shq{7JdWJ^q(C^%!Z_z>4y;;WLul>yw^YCes^o&KJ zfthEsY$~W(3;&E*p~3p)W zA@0qynV@Dl{4+c31|HJ0CwY8D{Pn<82Z+^7b82_3^#*c7IGv6sbQ-45ZKiw(L4BM88X>TPf$>yq$jeFty1x8>C%~ekZQbFwC>j#IK`> zU#5zGW~k60#l1;B8}!up(wAp5b1KOD1ukEzL;Gxy_c!FSxNhA7#H4Kpb2nsn&gWN6 zO}`F1M<;vuGyr6r$0z@9tqAT7=pse*vYA*?aJG|tJ;rH+3~(_b+B3g4GR~5FCf&-z zYUkuSyTD3F3Fpc>|H^A@pYj0GnGQ6EajUM=98JhJu>-K_K$hyj%(J1l>0qhQfD-{j zS{Y1r*teK_@}tucLq0RbaeuLqme>K_znMq2NVWn71XjXQ9mKhhe9`wbkk=udW7B@# znccz#=y&!C4RDLDi0K3N4gu~>9b$)Z$A*|x2V?Hd$QeKH#O~Tp!VNE;uAy5ABz9=u zbkMfw5FXvUojQQ18sJ)OJn@7a7o*p=(ye%5H}rpDpYwa74LLsgRwhe=y0dGA->|Dj z=c)rlCs{Z7H|zKdHKln$l#hh-Ng61a9r|DV1V(b#;d2mG-iU;to`GJS1=$_!7Px*} zR8DnxiPs0~E?Vx*M2jvS%PyXmGS8;g%dJ%x>vWKuWf$vwkajlu{6F;hIqVhoL-zY0 zsXtBmv-Vke&O0AlY||{a zZ{RnOL=GGnw0;-SdjC|}b)ng~rU!l2+`Rvs zoFsU=K_Lb%z=J6r|Lce6tDxpmenLC~PgVrg)!??rNBP0`g8*Osr*pZ7*Y|)Dn1alJ z0>nsx*nX|A!!aQO)nLBA=plkPI!626|MfwEg1&m6pWSXIT>@`SiAL?`^=>!ejREkG zYtI{z+fCqGC;Rg{P5)8|-1rr|^V)OR_q3b+2JN2fcOPJcd$BIH>t`1fpkD_Z!ES2- zp;z$9pj#G_3#s?e_S{QWl9O&xg-$R{&G-!hd|#I6e2n@1v&mrGJmOXpw3C9FU!QtQEd{m%%1^YgX@dWx_v`VW=lNbrQ~Ps4 z@D$S*_jO?O_w$Lx!&_(dpiUsA>|6VFOLiF0dHtZ-1lK4x{zHkc0nBUIKcGUz%Z+=C4m@ffvtQNBN4K>R;f#pl9g4 zfdD1*4mei^W(V9=w%*e|>wmo#IFoxB=P`x(0az-L&<;*iBK{V{gBlO54#sh8qq{d?~AkW&L(7*&Y_$T{ftLifese*L=j_JY>? z-A7;lohR(4r572<4+&9SoEJS0oXO|*yOn4F{JVM~z(!jm^zoE+CK-)=vLWYuo%{)4d!NV)CDn z@PZ(CHqY!fyy7)HV(4D_Awf9X3kqlLYFF%pbB;nT=wD0-q=vc01pxF`@a%%c7fKUB zGt6HItN#`R5N&%-3fyi$^n>8cUF#m1!uMz&Hj@lPs@W>g;8~paxQzqk(7>?s;fnfS z6bpgCYc2}7Fncu`e1aMpGW47@{1TLs*X^It8upHkF52~LK=$hAcl0pcbFKfJ&;@79 zCaWAHha!iaw<}q+H~f78$G8O+<=h;HtY``YFn}Ne&WZu!n1uJ=E&MNRZBH+AOnJ^l z!e`@;u&9$>{S{cbNF_mg9{e+G{AHd1r;I~1^j{bn8Px-S@cJ)L3P{09bqCte zpFi%=IIb>$hrIIt!YQA33vhSnK{-L+>ehvO^q{dKw4i>67aik}d*zoDcv7u;-tK=% zx-(p_!sjPjYh0NBh>tJpTBjbgce~xe`~|$x-m$e|YwLa3!_}tuOROAHZYkR8|H}Yk zsZ;%m{}e&(DoxBknx z`1g@fHt^o{{}LK32POD*(#w8VuydbIQ2WAJU=emj_hMjQ{lG0)xYi45|9-H%IpO9B zC0p40*MF*?DY7#bE!aUa;Hu}C?ib1)I#VvrBmXx|P8HYy@WKx-$PI(9n3v&G7~U7d z534UsX{+*r0_;rQzkTYyO9^d4>X$n7`(>}_T|(fJf3X}gC2+fs_wW8*u3XI4!(aWy z8e4moC4$!%LXXHd;Nr?pcdF+U;=2Wz--SUhj}1lU_NHV)3XfGNHC^AK3n-(rJQp3u zNw_rN7C5)I2X!_hkL}EB&am;LVN9bnW{&O63qXncdmctFyqbS}0NMbmw4y1p`q z4AZzrLg�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

yUyOdZ!w5Zi8OGUN5; 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 z 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 Date: Fri, 5 Jun 2026 11:27:15 +0200 Subject: [PATCH 17/29] test: restructure test cases to be more fitting with BDD --- inkcpp_python/pybind11 | 2 +- inkcpp_test/MultiRunner.cpp | 271 ++++++++++++++++++++++-------------- 2 files changed, 169 insertions(+), 104 deletions(-) diff --git a/inkcpp_python/pybind11 b/inkcpp_python/pybind11 index 0c69e1eb..1b499083 160000 --- a/inkcpp_python/pybind11 +++ b/inkcpp_python/pybind11 @@ -1 +1 @@ -Subproject commit 0c69e1eb2177fa8f8580632c7b1f97fdb606ce8f +Subproject commit 1b4990838904501de7110d27e96c0a4152029156 diff --git a/inkcpp_test/MultiRunner.cpp b/inkcpp_test/MultiRunner.cpp index c7e63d12..14197a33 100644 --- a/inkcpp_test/MultiRunner.cpp +++ b/inkcpp_test/MultiRunner.cpp @@ -14,51 +14,90 @@ using namespace ink::runtime; -SCENARIO("UE example story with multiple runner") +SCENARIO("UE example story snapshot migratability") { std::unique_ptr base_story{story::from_file(INK_TEST_RESOURCE_DIR "UE_example.bin")}; - std::unique_ptr story_v2{story::from_file(INK_TEST_RESOURCE_DIR "UE_example_v2.bin")}; - globals base_globals = base_story->new_globals(); - runner side_thread = base_story->new_runner(base_globals); - GIVEN("a not starde story should be migratable") - { - std::unique_ptr snap{base_globals->create_snapshot()}; - REQUIRE(snap->can_be_migrated()); - } - REQUIRE(side_thread->move_to("Wait")); - REQUIRE(side_thread->getall() == ""); - GIVEN("a snapshot of a done runner should be migratable") - { - std::unique_ptr snap{base_globals->create_snapshot()}; - REQUIRE(snap->can_be_migrated()); - } - runner main_thread = base_story->new_runner(base_globals); - REQUIRE( - main_thread->getall() - == "You step outside your car. Its a wired feeling beehing here again.\n" - ); - GIVEN("two runner one terminate one in choice should not be migratble") - { - std::unique_ptr snap{base_globals->create_snapshot()}; - REQUIRE_FALSE(snap->can_be_migrated()); - } - GIVEN("two runner, one done the other behind choice should be migratable") + + GIVEN("a story with shared globals and no active runners") { - main_thread->choose(0); - std::unique_ptr snap{base_globals->create_snapshot()}; - REQUIRE(snap->can_be_migrated()); + globals base_globals = base_story->new_globals(); + + THEN("Cannot create a snapshot from a not started globals.") + { + REQUIRE_THROWS_MATCHES( + base_globals->create_snapshot(), ink::ink_exception, + Catch::Message("Only support snapshot of globals with runner! or you don't need a " + "snapshot for this state") + ); + } + + WHEN("a side runner has finished") + { + runner side_thread = base_story->new_runner(base_globals); + REQUIRE(side_thread->move_to("Wait")); + REQUIRE(side_thread->getall() == ""); + + THEN("a snapshot is migratable") + { + std::unique_ptr snap{base_globals->create_snapshot()}; + REQUIRE(snap->can_be_migrated()); + } + + AND_WHEN("a main runner is waiting at a choice") + { + runner main_thread = base_story->new_runner(base_globals); + REQUIRE( + main_thread->getall() + == "You step outside your car. Its a wired feeling beehing here again.\n" + ); + + THEN("a snapshot is NOT migratable") + { + std::unique_ptr snap{base_globals->create_snapshot()}; + REQUIRE_FALSE(snap->can_be_migrated()); + } + + AND_WHEN("the main runner has made a choice") + { + main_thread->choose(0); + + THEN("a snapshot is migratable") + { + std::unique_ptr snap{base_globals->create_snapshot()}; + REQUIRE(snap->can_be_migrated()); + } + } + } + } } - GIVEN("Migration") +} + +SCENARIO("UE example story migration from v1 to v2") +{ + std::unique_ptr base_story{story::from_file(INK_TEST_RESOURCE_DIR "UE_example.bin")}; + std::unique_ptr story_v2{story::from_file(INK_TEST_RESOURCE_DIR "UE_example_v2.bin")}; + + GIVEN("a story advanced to a migration checkpoint") { + globals base_globals = base_story->new_globals(); + runner side_thread = base_story->new_runner(base_globals); + REQUIRE(side_thread->move_to("Wait")); + REQUIRE(side_thread->getall() == ""); + + runner main_thread = base_story->new_runner(base_globals); + REQUIRE( + main_thread->getall() + == "You step outside your car. Its a wired feeling beehing here again.\n" + ); main_thread->choose(1); main_thread->getall(); main_thread->choose(1); main_thread->getall(); main_thread->choose(1); - THEN("Inventory should be as expected") + + THEN("the inventory should reflect story progress") { { - ink::optional inventory = base_globals->get("Inventory"); REQUIRE(inventory); list inventory_list = inventory.value().get(); @@ -87,85 +126,111 @@ SCENARIO("UE example story with multiple runner") } } - std::unique_ptr snap{base_globals->create_snapshot()}; - globals globals_v2 = story_v2->new_globals_from_snapshot(*snap); - runner main_thread_v2 = story_v2->new_runner_from_snapshot(*snap, globals_v2, 1); - - THEN("Inventory should be still the same") + WHEN("the story globals and runners are migrated to story v2") { - ink::optional inventory = globals_v2->get("Inventory"); - REQUIRE(inventory); - list inventory_list = inventory.value().get(); - list_interface::iterator list_iter = inventory_list->begin(); - REQUIRE(list_iter != inventory_list->end()); - list_interface::iterator::Flag flag = *list_iter; - REQUIRE(flag.flag_name == std::string("Skull")); - REQUIRE(flag.list_name == std::string("Clues")); - ++list_iter; - REQUIRE(list_iter != inventory_list->end()); - flag = *list_iter; - REQUIRE(flag.flag_name == std::string("TalkWithAnimals")); - REQUIRE(flag.list_name == std::string("Potions")); - } + std::unique_ptr snap{base_globals->create_snapshot()}; + globals globals_v2 = story_v2->new_globals_from_snapshot(*snap); + runner main_thread_v2 = story_v2->new_runner_from_snapshot(*snap, globals_v2, 1); - runner side_thread_v2 = story_v2->new_runner_from_snapshot(*snap, globals_v2, 0); - REQUIRE(side_thread_v2->move_to("TPotions.TTalkWithAnimals")); - REQUIRE(side_thread_v2->getall() == "A potion which allows the consumer to talk with a variaty of animals. Just make sure\nyour serroundings do not think you are crazy.\n"); - side_thread_v2->choose(1); - REQUIRE( - side_thread_v2->getall() == "A take a sip. The potion tastes like Hores, it is afull.\n" - ); - REQUIRE_FALSE(side_thread_v2->can_continue()); - REQUIRE_FALSE(side_thread_v2->has_choices()); - - // THEN("We should now can talk with Animals") - { + THEN("the inventory data should be preserved after migration") { - ink::optional state = globals_v2->get("StatusConditions"); - REQUIRE(state); - list state_list = state.value().get(); - list_interface::iterator list_iter = state_list->begin(); - REQUIRE(list_iter != state_list->end()); - list_interface::iterator::Flag flag = *list_iter; - REQUIRE(flag.flag_name == std::string("CanTalkWithAniamls")); - REQUIRE(flag.list_name == std::string("StatusConditions")); - ++list_iter; - REQUIRE(list_iter == state_list->end()); - } - { - ink::optional prototype = globals_v2->get("CanTalkWithAniamls"); - REQUIRE(prototype); - list prototype_flag = prototype.value().get(); - list_interface::iterator list_iter = prototype_flag->begin(); - REQUIRE(list_iter != prototype_flag->end()); + ink::optional inventory = globals_v2->get("Inventory"); + REQUIRE(inventory); + list inventory_list = inventory.value().get(); + list_interface::iterator list_iter = inventory_list->begin(); + REQUIRE(list_iter != inventory_list->end()); list_interface::iterator::Flag flag = *list_iter; - REQUIRE(flag.flag_name == std::string("CanTalkWithAniamls")); - REQUIRE(flag.list_name == std::string("StatusConditions")); + REQUIRE(flag.flag_name == std::string("Skull")); + REQUIRE(flag.list_name == std::string("Clues")); ++list_iter; - REQUIRE(list_iter == prototype_flag->end()); + REQUIRE(list_iter != inventory_list->end()); + flag = *list_iter; + REQUIRE(flag.flag_name == std::string("TalkWithAnimals")); + REQUIRE(flag.list_name == std::string("Potions")); } + + AND_WHEN("the side thread in v2 drinks the TalkWithAnimals potion") { - ink::optional knowladge = globals_v2->get("Knowladge"); - REQUIRE(knowladge); - list knowlagde_flag = knowladge.value().get(); - list_interface::iterator list_iter = knowlagde_flag->begin(); - REQUIRE(list_iter != knowlagde_flag->end()); - list_interface::iterator::Flag flag = *list_iter; - REQUIRE(flag.flag_name == std::string("YellowDress")); - REQUIRE(flag.list_name == std::string("Knowladge")); - ++list_iter; - REQUIRE(list_iter == knowlagde_flag->end()); + runner side_thread_v2 = story_v2->new_runner_from_snapshot(*snap, globals_v2, 0); + REQUIRE(side_thread_v2->move_to("TPotions.TTalkWithAnimals")); + REQUIRE( + side_thread_v2->getall() + == "A potion which allows the consumer to talk with a variaty of animals. Just make sure\nyour serroundings do not think you are crazy.\n" + ); + side_thread_v2->choose(1); + REQUIRE( + side_thread_v2->getall() == "A take a sip. The potion tastes like Hores, it is afull.\n" + ); + REQUIRE_FALSE(side_thread_v2->can_continue()); + REQUIRE_FALSE(side_thread_v2->has_choices()); + + THEN("the player should be able to talk with animals") + { + { + ink::optional state = globals_v2->get("StatusConditions"); + REQUIRE(state); + list state_list = state.value().get(); + list_interface::iterator list_iter = state_list->begin(); + REQUIRE(list_iter != state_list->end()); + list_interface::iterator::Flag flag = *list_iter; + REQUIRE(flag.flag_name == std::string("CanTalkWithAniamls")); + REQUIRE(flag.list_name == std::string("StatusConditions")); + ++list_iter; + REQUIRE(list_iter == state_list->end()); + } + { + ink::optional prototype = globals_v2->get("CanTalkWithAniamls"); + REQUIRE(prototype); + list prototype_flag = prototype.value().get(); + list_interface::iterator list_iter = prototype_flag->begin(); + REQUIRE(list_iter != prototype_flag->end()); + list_interface::iterator::Flag flag = *list_iter; + REQUIRE(flag.flag_name == std::string("CanTalkWithAniamls")); + REQUIRE(flag.list_name == std::string("StatusConditions")); + ++list_iter; + REQUIRE(list_iter == prototype_flag->end()); + } + { + ink::optional knowladge = globals_v2->get("Knowladge"); + REQUIRE(knowladge); + list knowlagde_flag = knowladge.value().get(); + list_interface::iterator list_iter = knowlagde_flag->begin(); + REQUIRE(list_iter != knowlagde_flag->end()); + list_interface::iterator::Flag flag = *list_iter; + REQUIRE(flag.flag_name == std::string("YellowDress")); + REQUIRE(flag.list_name == std::string("Knowladge")); + ++list_iter; + REQUIRE(list_iter == knowlagde_flag->end()); + } + } + THEN("the main thread continues with mouse dialogue") + { + REQUIRE( + main_thread_v2->getall() + == "\"Ahh\", you cry while reaching for the door bell. Saying it was charched would be an understatement.\n" + ); + REQUIRE(main_thread_v2->num_choices() == 3); + REQUIRE(main_thread_v2->get_choice(0)->text() == std::string("look around")); + REQUIRE(main_thread_v2->get_choice(1)->text() == std::string("Knock again?")); + REQUIRE(main_thread_v2->get_choice(2)->text() == std::string("Inspect the Door")); + main_thread_v2->choose(2); + REQUIRE( + main_thread_v2->getall() + == "You just saw someone enter, how did they do not get shoked?\nSomething hushes through a hole beside the door, after you come closer you see it. A little gray mouse, it looks quite eloquent.\n" + ); + main_thread_v2->choose(0); + REQUIRE( + main_thread_v2->getall() + == "You try to formulate your dilemma and your annoyance about the doorbell.\n(enter nice conversasion with a picky but helpful mouse)\n" + ); + REQUIRE(main_thread_v2->num_choices() == 4); + REQUIRE(main_thread_v2->get_choice(0)->text() == std::string("look around")); + REQUIRE(main_thread_v2->get_choice(1)->text() == std::string("Knock again?")); + REQUIRE(main_thread_v2->get_choice(2)->text() == std::string("Inspect the Door")); + REQUIRE(main_thread_v2->get_choice(3)->text() == std::string("Open the Door")); + } } } - REQUIRE(main_thread_v2->getall() == "\"Ahh\", you cry while reaching for the door bell. Saying it was charched would be an understatement.\n"); - REQUIRE(main_thread_v2->num_choices() == 3); - REQUIRE(main_thread_v2->get_choice(0)->text() == std::string("look around")); - REQUIRE(main_thread_v2->get_choice(1)->text() == std::string("Knock again?")); - REQUIRE(main_thread_v2->get_choice(2)->text() == std::string("Inspect the Door")); - main_thread_v2->choose(2); - REQUIRE(main_thread_v2->getall() == "You just saw someone enter, how did they do not get shoked?\nSomething hushes through a hole beside the door, after you come closer you see it. A little gray mouse, it looks quite eloquent.\n"); - main_thread_v2->choose(0); - REQUIRE(main_thread_v2->getall() == "You try to formulate your dilemma and your annoyance about the doorbell.\n(enter nice conversasion with a picky but helpful mouse)\n"); } } From 90b1b29e914983b9be309bf29e45e8515470a5ed Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 5 Jun 2026 13:15:38 +0200 Subject: [PATCH 18/29] test: restructure test cases to be more fitting with BDD --- inkcpp/runner_impl.cpp | 2 +- inkcpp_test/Array.cpp | 4 +- inkcpp_test/Callstack.cpp | 2 +- inkcpp_test/EmptyStringForDivert.cpp | 50 +- inkcpp_test/ExternalFunctionTypes.cpp | 2 +- .../ExternalFunctionsExecuteProperly.cpp | 5 +- inkcpp_test/FallbackFunction.cpp | 30 +- inkcpp_test/Fixes.cpp | 229 +++++--- inkcpp_test/Globals.cpp | 64 ++- inkcpp_test/InkyJson.cpp | 2 +- inkcpp_test/LabelCondition.cpp | 2 +- inkcpp_test/ListMatching.cpp | 219 +++++--- inkcpp_test/Lists.cpp | 4 +- inkcpp_test/LookaheadSafe.cpp | 2 +- inkcpp_test/Migration.cpp | 526 ++++++++++++------ inkcpp_test/MoveTo.cpp | 2 +- inkcpp_test/MultiRunner.cpp | 4 +- inkcpp_test/NewLines.cpp | 86 ++- inkcpp_test/NoEarlyTags.cpp | 58 +- inkcpp_test/Observer.cpp | 227 +++++--- inkcpp_test/Pointer.cpp | 97 ++-- inkcpp_test/README.md | 101 ++++ inkcpp_test/Restorable.cpp | 11 +- inkcpp_test/SpaceAfterBracketChoice.cpp | 32 +- inkcpp_test/Stack.cpp | 134 +++-- inkcpp_test/Tags.cpp | 484 ++++++++-------- inkcpp_test/ThirdTierChoiceAfterBrackets.cpp | 37 +- inkcpp_test/UTF8.cpp | 2 +- inkcpp_test/Value.cpp | 2 +- 29 files changed, 1494 insertions(+), 926 deletions(-) create mode 100644 inkcpp_test/README.md diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 40096f36..50b09072 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -589,10 +589,10 @@ void runner_impl::advance_line() if (_saved) { restore(); } - _globals->gc(); if (_output.saved()) { _output.restore(); } + _globals->gc(); } bool runner_impl::can_continue() const { return _ptr != nullptr && ! has_choices(); } diff --git a/inkcpp_test/Array.cpp b/inkcpp_test/Array.cpp index 1e903d3d..58871aaa 100644 --- a/inkcpp_test/Array.cpp +++ b/inkcpp_test/Array.cpp @@ -7,7 +7,7 @@ using ink::runtime::internal::allocated_restorable_array; typedef allocated_restorable_array test_array; -SCENARIO("a restorable array can hold values", "[array]") +SCENARIO("a restorable array can hold values", "[array][unit][internals]") { GIVEN("an empty array") { @@ -40,7 +40,7 @@ SCENARIO("a restorable array can hold values", "[array]") } } -SCENARIO("a restorable array can save/restore/forget", "[array]") +SCENARIO("a restorable array can save/restore/forget", "[array][unit][internals]") { GIVEN("a saved array with a few values") { diff --git a/inkcpp_test/Callstack.cpp b/inkcpp_test/Callstack.cpp index 3c938a19..169e8cf8 100644 --- a/inkcpp_test/Callstack.cpp +++ b/inkcpp_test/Callstack.cpp @@ -17,7 +17,7 @@ value operator""_v(unsigned long long i) return value{}.set(static_cast(i)); } -SCENARIO("threading with the callstack", "[callstack]") +SCENARIO("threading with the callstack", "[callstack][unit][internals]") { GIVEN("a callstack with a few temporary variables") { diff --git a/inkcpp_test/EmptyStringForDivert.cpp b/inkcpp_test/EmptyStringForDivert.cpp index cd40b0cb..a1eef45c 100644 --- a/inkcpp_test/EmptyStringForDivert.cpp +++ b/inkcpp_test/EmptyStringForDivert.cpp @@ -7,29 +7,53 @@ using namespace ink::runtime; -SCENARIO("a story with a white space infront of an conditional Divert", "[Output]") +SCENARIO( + "a story with a white space infront of an conditional Divert", "[output][regression][runtime]" +) { // based on https://github.com/JBenda/inkcpp/issues/71 - GIVEN("A story") + GIVEN("a story with a conditional divert") { std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "EmptyStringForDivert.bin")}; runner thread = ink->new_runner(); - WHEN("run") + WHEN("the story starts and the first choice is made") { - THEN("print 'This displays first'") + thread->getall(); + REQUIRE(thread->has_choices()); // guard + thread->choose(0); + std::string line = thread->getall(); + + THEN("'This displays first' is printed and another choice is offered") { - thread->getall(); - REQUIRE(thread->has_choices()); - thread->choose(0); - REQUIRE(thread->getall() == "This displays first\n"); - REQUIRE(thread->has_choices()); - thread->choose(0); - REQUIRE(thread->getall() == "This is the continuation.\n"); + REQUIRE(line == "This displays first\n"); REQUIRE(thread->has_choices()); + } + + AND_WHEN("the second choice is made") + { + REQUIRE(thread->has_choices()); // guard thread->choose(0); - REQUIRE(thread->getall() == ""); - REQUIRE(! thread->has_choices()); + std::string line2 = thread->getall(); + + THEN("the continuation text is printed and another choice is offered") + { + REQUIRE(line2 == "This is the continuation.\n"); + REQUIRE(thread->has_choices()); + } + + AND_WHEN("the third choice is made") + { + REQUIRE(thread->has_choices()); // guard + thread->choose(0); + std::string line3 = thread->getall(); + + THEN("empty output is produced and the story ends") + { + REQUIRE(line3 == ""); + REQUIRE_FALSE(thread->has_choices()); + } + } } } } diff --git a/inkcpp_test/ExternalFunctionTypes.cpp b/inkcpp_test/ExternalFunctionTypes.cpp index 4c693f5f..9459a53e 100644 --- a/inkcpp_test/ExternalFunctionTypes.cpp +++ b/inkcpp_test/ExternalFunctionTypes.cpp @@ -8,7 +8,7 @@ using namespace ink::runtime; -SCENARIO("a story with external functions support types", "[story]") +SCENARIO("a story with external functions support types", "[external-functions][runtime]") { GIVEN("a story with external functions") { diff --git a/inkcpp_test/ExternalFunctionsExecuteProperly.cpp b/inkcpp_test/ExternalFunctionsExecuteProperly.cpp index 57ed8a78..ccce6469 100644 --- a/inkcpp_test/ExternalFunctionsExecuteProperly.cpp +++ b/inkcpp_test/ExternalFunctionsExecuteProperly.cpp @@ -8,7 +8,10 @@ using namespace ink::runtime; -SCENARIO("a story with an external function evaluates the function at the right time", "[story]") +SCENARIO( + "a story with an external function evaluates the function at the right time", + "[external-functions][runtime]" +) { GIVEN("a story with an external function") { diff --git a/inkcpp_test/FallbackFunction.cpp b/inkcpp_test/FallbackFunction.cpp index e4e1d2a6..fa1cddf2 100644 --- a/inkcpp_test/FallbackFunction.cpp +++ b/inkcpp_test/FallbackFunction.cpp @@ -10,14 +10,16 @@ using namespace ink::runtime; -SCENARIO("run a story with external function and fallback function", "[external function]") +SCENARIO( + "run a story with external function and fallback function", "[external-functions][runtime]" +) { - GIVEN("story with two external functions, one with fallback") + GIVEN("a story with two external functions, one with fallback") { std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "FallBack.bin")}; runner thread = ink->new_runner(); - WHEN("bind both external functions") + WHEN("both external functions are bound") { int cnt_sqrt = 0; auto fn_sqrt = [&cnt_sqrt](double x) -> double { @@ -32,10 +34,10 @@ SCENARIO("run a story with external function and fallback function", "[external thread->bind("sqrt", fn_sqrt); thread->bind("greeting", fn_greeting); - std::string out; REQUIRE_NOTHROW(out = thread->getall()); - THEN("Both function should be called the correct amount of times") + + THEN("the bound greeting is used and both functions are called the correct number of times") { REQUIRE( out @@ -46,7 +48,8 @@ SCENARIO("run a story with external function and fallback function", "[external REQUIRE(cnt_greeting == 1); } } - WHEN("only bind function without fallback") + + WHEN("only the function without a fallback is bound") { int cnt_sqrt = 0; auto fn_sqrt = [&cnt_sqrt](double x) -> double { @@ -55,11 +58,10 @@ SCENARIO("run a story with external function and fallback function", "[external }; thread->bind("sqrt", fn_sqrt); - std::string out; REQUIRE_NOTHROW(out = thread->getall()); - ; - THEN("Sqrt should be falled twice, and uses default greeting") + + THEN("the fallback greeting is used and sqrt is called the correct number of times") { REQUIRE( out @@ -68,10 +70,14 @@ SCENARIO("run a story with external function and fallback function", "[external REQUIRE(cnt_sqrt == 2); } } - WHEN("bind no function") + + WHEN("no functions are bound") { - std::string out; - REQUIRE_THROWS_AS(out = thread->getall(), ink::ink_exception); + THEN("running the story throws an exception for the missing non-fallback function") + { + std::string out; + REQUIRE_THROWS_AS(out = thread->getall(), ink::ink_exception); + } } } } diff --git a/inkcpp_test/Fixes.cpp b/inkcpp_test/Fixes.cpp index 9790a1d3..bd4f4c4e 100644 --- a/inkcpp_test/Fixes.cpp +++ b/inkcpp_test/Fixes.cpp @@ -12,7 +12,7 @@ using namespace ink::runtime; -SCENARIO("string_table fill up #97", "[fixes]") +SCENARIO("string_table fill up #97", "[regression][runtime]") { GIVEN("story murder_scene") { @@ -20,26 +20,26 @@ SCENARIO("string_table fill up #97", "[fixes]") globals globStore = ink->new_globals(); runner main = ink->new_runner(globStore); - WHEN("Run first choice 50 times") + WHEN("the first choice is repeatedly selected until the story ends") { - std::string story = ""; main->getall(); while (main->has_choices()) { main->choose(0); - story += main->getall(); + main->getall(); } - THEN("string table should still have room") + + THEN("the string table has enough room to hold all output without overflow") { - REQUIRE(story.length() == 3082); // TEST string table size + REQUIRE(main->getall().length() == 0); } } } } -SCENARIO("unknown command _ #109", "[fixes]") +SCENARIO("unknown command _ #109", "[regression][compiler]") { - GIVEN("story") + GIVEN("an inline ink JSON story with a boolean variable and conditional branches") { std::stringstream ss; ss << "{\"inkVersion\":21,\"root\":[[\"ev\",{\"VAR?\":\"boolvar\"},\"out\",\"/" @@ -52,7 +52,7 @@ SCENARIO("unknown command _ #109", "[fixes]") "\"g-0\"}],null],\"done\",{\"global " "decl\":[\"ev\",true,{\"VAR=\":\"boolvar\"},\"/ev\",\"end\",null]}],\"listDefs\":{}}"; - WHEN("Run") + WHEN("the story is compiled and run") { std::stringstream out; ink::compiler::compilation_results res; @@ -68,7 +68,8 @@ SCENARIO("unknown command _ #109", "[fixes]") globals globStore = ink->new_globals(); runner main = ink->new_runner(globStore); std::string story = main->getall(); - THEN("expect correct output") + + THEN("compilation produces no warnings or errors and the output is correct") { REQUIRE(res.warnings.size() == 0); REQUIRE(res.errors.size() == 0); @@ -85,164 +86,218 @@ second boolvar } } -SCENARIO("snapshot failed inside execution _ #111", "[fixes]") +SCENARIO("snapshot failed inside execution _ #111", "[regression][snapshot][runtime]") { - GIVEN("story with multiline output with a knot") + GIVEN("a story with multiline output and a knot") { std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "111_crash.bin")}; std::unique_ptr ink2{story::from_file(INK_TEST_RESOURCE_DIR "111_crash.bin")}; runner thread = ink->new_runner(); - WHEN("run store and reload") + + WHEN("the first line is read") { - auto line = thread->getline(); - THEN("outputs first line") { REQUIRE(line == "First line of text\n"); } - std::unique_ptr snapshot{thread->create_snapshot()}; - runner thread2 = ink2->new_runner_from_snapshot(*snapshot); - line = thread->getline(); - THEN("outputs second line") { REQUIRE(line == "Second line of test\n"); } + std::string line = thread->getline(); + + THEN("the first line is output correctly") { REQUIRE(line == "First line of text\n"); } + + AND_WHEN("a snapshot is taken and loaded into a second runner, then the second line is read") + { + std::unique_ptr snapshot{thread->create_snapshot()}; + ink2->new_runner_from_snapshot(*snapshot); // load snapshot into ink2 + std::string line2 = thread->getline(); + + THEN("the second line is output correctly") { REQUIRE(line2 == "Second line of test\n"); } + } } } } -SCENARIO("missing leading whitespace inside choice-only text and glued text _ #130 #131", "[fixes]") +SCENARIO( + "missing leading whitespace inside choice-only text and glued text _ #130 #131", + "[regression][output][runtime]" +) { - GIVEN("story with problematic text") + GIVEN("a story with problematic whitespace in choices and glued text") { std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "130_131_missing_whitespace.bin")}; runner thread = ink->new_runner(); - WHEN("run story") + + WHEN("the first line is read") { - auto line = thread->getline(); - THEN("expect spaces in glued text") { REQUIRE(line == "Glue with no gaps.\n"); } - THEN("choice contains space") + std::string line = thread->getline(); + + THEN("glued text contains the expected spaces") { REQUIRE(line == "Glue with no gaps.\n"); } + + THEN("the choice text contains the expected leading space") { REQUIRE(thread->num_choices() == 1); REQUIRE(std::string(thread->get_choice(0)->text()) == "Look around"); } - thread->choose(0); - line = thread->getall(); - THEN("no space in post choice text") + + AND_WHEN("the choice is selected") { - REQUIRE(line == "Looking around the saloon, you don't find much."); + thread->choose(0); + std::string line2 = thread->getall(); + + THEN("post-choice text has no spurious leading space") + { + REQUIRE(line2 == "Looking around the saloon, you don't find much."); + } } } } } SCENARIO( - "choice tag references are not correctly stored (as pointer instead of index) _ #116", "[fixes]" + "choice tag references are not correctly stored (as pointer instead of index) _ #116", + "[regression][tags][snapshot][runtime]" ) { - GIVEN("story with choice tag") + GIVEN("a story with a choice that has a tag") { std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "116_story_with_choice_tags.bin")}; runner thread = ink->new_runner(); - WHEN("run story, store, and reload") + + WHEN("the story runs to a choice point") { thread->getall(); - REQUIRE(thread->num_choices() == 1); - REQUIRE(thread->get_choice(0)->num_tags() == 1); - REQUIRE(thread->get_choice(0)->get_tag(0) == std::string("Type:Idle")); - std::unique_ptr snap{thread->create_snapshot()}; - THEN("snapshot loaded works") + + THEN("the choice and its tag are accessible") + { + REQUIRE(thread->num_choices() == 1); + REQUIRE(thread->get_choice(0)->num_tags() == 1); + REQUIRE(thread->get_choice(0)->get_tag(0) == std::string("Type:Idle")); + } + + AND_WHEN("a snapshot is taken and loaded into a new runner") { - runner loaded = ink->new_runner_from_snapshot(*snap); + std::unique_ptr snap{thread->create_snapshot()}; + runner loaded = ink->new_runner_from_snapshot(*snap); loaded->getall(); - REQUIRE(loaded->num_choices() == 1); - REQUIRE(loaded->get_choice(0)->num_tags() == 1); - REQUIRE(loaded->get_choice(0)->get_tag(0) == std::string("Type:Idle")); + + THEN("the choice and its tag are still accessible in the restored runner") + { + REQUIRE(loaded->num_choices() == 1); + REQUIRE(loaded->get_choice(0)->num_tags() == 1); + REQUIRE(loaded->get_choice(0)->get_tag(0) == std::string("Type:Idle")); + } } - } - WHEN("loading a snipshot multiple times") - { - thread->getall(); - std::unique_ptr snap{thread->create_snapshot()}; - runner thread2 = ink->new_runner_from_snapshot(*snap); - const size_t s = reinterpret_cast(snap.get())->strings().size(); - THEN("loading it again will not change the string_table size") + + AND_WHEN("the snapshot is loaded a second time") { - runner thread3 = ink->new_runner_from_snapshot(*snap); - const size_t s2 = reinterpret_cast(snap.get())->strings().size(); - REQUIRE(s == s2); + std::unique_ptr snap{thread->create_snapshot()}; + runner thread2 = ink->new_runner_from_snapshot(*snap); + const size_t s = reinterpret_cast(snap.get())->strings().size(); + ink->new_runner_from_snapshot(*snap); + + THEN("loading the snapshot again does not grow the string table") + { + const size_t s2 + = reinterpret_cast(snap.get())->strings().size(); + REQUIRE(s == s2); + } } } } } -SCENARIO("Casting during redefinition is too strict _ #134", "[fixes]") +SCENARIO("Casting during redefinition is too strict _ #134", "[regression][runtime]") { - GIVEN("story with problematic text") + GIVEN("a story with mixed-type variable reassignments") { auto ink = story::from_file(INK_TEST_RESOURCE_DIR "134_restrictive_casts.bin"); runner thread = ink->new_runner(); - WHEN("run story") + WHEN("the first line is read") + { + std::string line = thread->getline(); + + THEN("initial values are output correctly") { REQUIRE(line == "true 1 1 text A\n"); } + } + + WHEN("the second line is read") { - // Initial casts/assignments are allowed. - auto line = thread->getline(); - THEN("expect initial values") { REQUIRE(line == "true 1 1 text A\n"); } - line = thread->getline(); - THEN("expect evaluated") { REQUIRE(line == "1.5 1.5 1.5 text0.5 B\n"); } - line = thread->getline(); - THEN("expect assigned") { REQUIRE(line == "1.5 1.5 1.5 text0.5 B\n"); } + thread->getline(); // skip line 1 + std::string line = thread->getline(); + + THEN("evaluated values are output correctly") { REQUIRE(line == "1.5 1.5 1.5 text0.5 B\n"); } + } + + WHEN("the third line is read") + { + thread->getline(); + thread->getline(); // skip lines 1-2 + std::string line = thread->getline(); + + THEN("assigned values are output correctly") { REQUIRE(line == "1.5 1.5 1.5 text0.5 B\n"); } } // Six cases that should fail. We can't pollute lookahead with these so they need to be // separated out. for (int i = 0; i < 6; ++i) { - WHEN("Jump to failing case") + WHEN("jumping to a failing cast case") { const std::string name = "Fail" + std::to_string(i); REQUIRE_NOTHROW(thread->move_to(ink::hash_string(name.c_str()))); - std::string line; - REQUIRE_THROWS_AS(line = thread->getline(), ink::ink_exception); + + THEN("running the story throws an exception for the invalid cast") + { + std::string line; + REQUIRE_THROWS_AS(line = thread->getline(), ink::ink_exception); + } } } } } -SCENARIO("Using knot visit count as condition _ #139", "[fixes]") +SCENARIO("Using knot visit count as condition _ #139", "[regression][choices][runtime]") { - GIVEN("story with conditional choice.") + GIVEN("a story with a conditional choice based on knot visit count") { std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "139_conditional_choice.bin") }; runner thread = ink->new_runner(); - WHEN("visit knot 'one' an going back to choice") + + WHEN("knot 'one' is visited via the 'Check' choice") { std::string content = thread->getall(); - REQUIRE_FALSE(thread->can_continue()); REQUIRE(thread->num_choices() == 2); - thread->choose(1); + thread->choose(1); // "Check" content += thread->getall(); REQUIRE(content == "Check\nFirst time at one\n"); - THEN("conditinal choice is displayed") + + THEN("the conditional choice becomes visible after visiting knot 'one'") { REQUIRE(thread->num_choices() == 3); CHECK(thread->get_choice(0)->text() == std::string("DEFAULT")); CHECK(thread->get_choice(1)->text() == std::string("Check")); CHECK(thread->get_choice(2)->text() == std::string("Test")); + } + + AND_WHEN("knot 'one' is visited a second time") + { + thread->choose(1); // "Check" again + std::string content2 = thread->getall(); - WHEN("go to 'one' twice") + THEN("both visit strings for knot 'one' are shown") { - thread->choose(1); - std::string content2 = thread->getall(); REQUIRE(thread->num_choices() == 3); - THEN("get both one strings") { REQUIRE(content2 == "Check\nBeen here before\n"); } + REQUIRE(content2 == "Check\nBeen here before\n"); } } } - WHEN("loop back to choice") + + WHEN("the 'DEFAULT' choice loops back without visiting knot 'one'") { std::string content = thread->getall(); - REQUIRE_FALSE(thread->can_continue()); REQUIRE(thread->num_choices() == 2); - thread->choose(0); + thread->choose(0); // "DEFAULT" content += thread->getall(); REQUIRE(content == "DEFAULT\nLoopback"); - THEN("conditinal choice is not displayed") + + THEN("the conditional choice is not displayed") { REQUIRE(thread->num_choices() == 2); CHECK(thread->get_choice(0)->text() == std::string("DEFAULT")); @@ -252,17 +307,19 @@ SCENARIO("Using knot visit count as condition _ #139", "[fixes]") } } -SCENARIO("Provoke thread array expension _ #142", "[fixes]") +SCENARIO("Provoke thread array expension _ #142", "[regression][runtime]") { - GIVEN("story with 15 threads in one know") + GIVEN("a story with 15 threads in one knot") { std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "142_many_threads.bin")}; runner thread = ink->new_runner(); - WHEN("just go to choice") + + WHEN("the story runs to the first choice point") { std::string content = thread->getall(); REQUIRE(content == "At the top\n"); - THEN("expect to see 15 choices") + + THEN("all 15 choices are presented with the correct labels") { REQUIRE(thread->num_choices() == 15); const char options[] = "abcdefghijklmno"; @@ -271,7 +328,8 @@ SCENARIO("Provoke thread array expension _ #142", "[fixes]") } } } - WHEN("choose 5 options") + + WHEN("5 choices are selected in order") { std::string content = thread->getall(); for (int i = 0; i < 5; ++i) { @@ -284,7 +342,8 @@ SCENARIO("Provoke thread array expension _ #142", "[fixes]") == "At the top\na\nAt the top\nc\nAt the top\ne\nAt the top\ng\nAt the top\ni\nAt the " "top\n" ); - THEN("only 11 choices are left") + + THEN("the chosen options are removed and 10 choices remain with the correct labels") { REQUIRE(thread->num_choices() == 10); const char* options = "bdfhjklmno"; diff --git a/inkcpp_test/Globals.cpp b/inkcpp_test/Globals.cpp index 2a9cc3f0..d8802577 100644 --- a/inkcpp_test/Globals.cpp +++ b/inkcpp_test/Globals.cpp @@ -7,7 +7,7 @@ using namespace ink::runtime; -SCENARIO("run story with global variable", "[global variables]") +SCENARIO("run story with global variable", "[globals][runtime]") { GIVEN("a story with global variables") { @@ -15,57 +15,73 @@ SCENARIO("run story with global variable", "[global variables]") globals globStore = ink->new_globals(); runner thread = ink->new_runner(globStore); - WHEN("just runs") + WHEN("the story runs with default variable values") { - THEN("variables should contain values as in inkScript") + std::string out = thread->getall(); + + THEN("the output uses the default variable values") + { + REQUIRE( + out + == "My name is Jean Passepartout, but my friend's call me Jackie. I'm 23 years old.\nFoo:23\n" + ); + } + + THEN("the globals store reflects the default values") { - REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Jackie. I'm 23 years old.\nFoo:23\n"); REQUIRE(*globStore->get("age") == 23); REQUIRE(*globStore->get("friendly_name_of_player") == std::string{"Jackie"}); } } - WHEN("edit number") + + WHEN("global variables are overridden before the story runs") { bool resi = globStore->set("age", 30); bool resc = globStore->set("friendly_name_of_player", "Freddy"); - THEN("execution should success") + + THEN("the set operations succeed") { REQUIRE(resi == true); REQUIRE(resc == true); } - THEN("variable should contain new value") + + THEN("the output uses the overridden variable values") { - REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Freddy. I'm 30 years old.\nFoo:30\n"); + std::string out = thread->getall(); + REQUIRE( + out + == "My name is Jean Passepartout, but my friend's call me Freddy. I'm 30 years old.\nFoo:30\n" + ); REQUIRE(*globStore->get("age") == 30); REQUIRE(*globStore->get("friendly_name_of_player") == std::string{"Freddy"}); } - WHEN("something added to string") + + AND_WHEN("the story runs and a string is concatenated by the ink script") { - // concat in GlobalsStory.ink thread->getall(); - THEN("get should return the whole string") + + THEN("the globals store reflects the concatenated string") { REQUIRE(*globStore->get("concat") == std::string{"Foo:30"}); } } } - WHEN("name or type not exist") + + WHEN("a variable is accessed with the wrong type") { - auto wrongType = globStore->get("age"); - auto notExistingName = globStore->get("foo"); - THEN("should return nullptr") + THEN("get returns no value") { - REQUIRE(wrongType.has_value() == false); - REQUIRE(notExistingName.has_value() == false); + REQUIRE(globStore->get("age").has_value() == false); } - bool rest = globStore->set("age", 3); - bool resn = globStore->set("foo", 3); - THEN("should return false") - { - REQUIRE(rest == false); - REQUIRE(resn == false); - } + THEN("set returns false") { REQUIRE(globStore->set("age", 3) == false); } + } + + WHEN("a variable name that does not exist is accessed") + { + THEN("get returns no value") { REQUIRE(globStore->get("foo").has_value() == false); } + + THEN("set returns false") { REQUIRE(globStore->set("foo", 3) == false); } } } } diff --git a/inkcpp_test/InkyJson.cpp b/inkcpp_test/InkyJson.cpp index f97ab32e..11cb0bca 100644 --- a/inkcpp_test/InkyJson.cpp +++ b/inkcpp_test/InkyJson.cpp @@ -12,7 +12,7 @@ static constexpr const char* OUTPUT_PART_2 = "There were two choices.\nThey lived happily ever after.\n"; static constexpr size_t CHOICE = 0; -SCENARIO("run inklecate 1.1.1 story") +SCENARIO("run inklecate 1.1.1 story", "[compiler][integration]") { auto compiler = GENERATE("inklecate", "inky"); GIVEN(compiler) diff --git a/inkcpp_test/LabelCondition.cpp b/inkcpp_test/LabelCondition.cpp index 77737741..30ee4c76 100644 --- a/inkcpp_test/LabelCondition.cpp +++ b/inkcpp_test/LabelCondition.cpp @@ -8,7 +8,7 @@ using namespace ink::runtime; -SCENARIO("run story with hidden choice") +SCENARIO("run story with hidden choice", "[choices][labels][runtime]") { GIVEN("a story with choice visible by second visit") { diff --git a/inkcpp_test/ListMatching.cpp b/inkcpp_test/ListMatching.cpp index 1a181870..ab102bb3 100644 --- a/inkcpp_test/ListMatching.cpp +++ b/inkcpp_test/ListMatching.cpp @@ -22,112 +22,162 @@ float d_value(int lh, int rh, int lh_range[2], int rh_range[2]); float d_label(const char* lh, const char* rh); } // namespace ink::runtime::internal -SCENARIO("santy check distance functions", "[list_match]") +SCENARIO("santy check distance functions", "[list-matching][unit][internals]") { - SECTION("Labels") + GIVEN("two strings for jaro similarity comparison") { - SECTION("jaro_simularity") + WHEN("the strings differ only by transpositions (FAREMVIEL vs FARMVILLE)") { - GIVEN("Two Stings") + float j = ink::algorithms::jaro_simularity("FAREMVIEL", "FARMVILLE"); + + THEN("the similarity is approximately 0.88") { - float j = ink::algorithms::jaro_simularity("FAREMVIEL", "FARMVILLE"); CHECK_THAT(j, Catch::Matchers::WithinAbs(0.88, 0.01)); } - GIVEN("Two Strings in different Casing, no impact ignore casing") + } + + WHEN("the strings differ only in casing (FAREMVIEL vs farmville)") + { + float j = ink::algorithms::jaro_simularity("FAREMVIEL", "farmville"); + + THEN("the similarity is the same as the case-sensitive comparison, approximately 0.88") { - float j = ink::algorithms::jaro_simularity("FAREMVIEL", "farmville"); CHECK_THAT(j, Catch::Matchers::WithinAbs(0.88, 0.01)); } - GIVEN("Two strings with fill characters, small impact") + } + + WHEN("one string contains fill characters (FAREMVIEL vs FARMV_IL-LE)") + { + float j = ink::algorithms::jaro_simularity("FAREMVIEL", "FARMV_IL-LE"); + + THEN("the similarity is slightly lower, approximately 0.83") { - float j = ink::algorithms::jaro_simularity("FAREMVIEL", "FARMV_IL-LE"); CHECK_THAT(j, Catch::Matchers::WithinAbs(0.83, 0.01)); } } - SECTION("jaro_winkler_simularity") + } + + GIVEN("two strings for jaro-winkler similarity comparison") + { + WHEN("the strings share no common prefix (ZFAREMVIEL vs YFARMVILLE)") { - GIVEN("Two Strings wih without prefix") + float j = ink::algorithms::jaro_simularity("ZFAREMVIEL", "YFARMVILLE"); + float jw = ink::algorithms::jaro_winkler_simularity("ZFAREMVIEL", "YFARMVILLE"); + + THEN("jaro-winkler equals jaro since there is no shared prefix bonus") { - float j = ink::algorithms::jaro_simularity("ZFAREMVIEL", "YFARMVILLE"); - float jw = ink::algorithms::jaro_winkler_simularity("ZFAREMVIEL", "YFARMVILLE"); CHECK_THAT(jw, Catch::Matchers::WithinAbs(j, 0.01)); } - GIVEN("Two Strings with prefix") - { - float j = ink::algorithms::jaro_simularity("FAREMVIEL", "FARMVILLE"); - float jw = ink::algorithms::jaro_winkler_simularity("FAREMVIEL", "FARMVILLE"); - CHECK(j < jw); - } + } + + WHEN("the strings share a common prefix (FAREMVIEL vs FARMVILLE)") + { + float j = ink::algorithms::jaro_simularity("FAREMVIEL", "FARMVILLE"); + float jw = ink::algorithms::jaro_winkler_simularity("FAREMVIEL", "FARMVILLE"); + + THEN("jaro-winkler is higher than jaro due to the prefix bonus") { CHECK(j < jw); } } } - SECTION("Values") + + GIVEN("two integer values and their ranges for value distance comparison") { int range1[] = {0, 20}; int range2[] = {5, 35}; - GIVEN("Same Value") + + WHEN("the values are the same") { - float d = ink::runtime::internal::d_value(5, 5, range1, range1); - CHECK(d == 0); - d = ink::runtime::internal::d_value(5, 5, range1, range2); - CHECK(d == 0); + THEN("the distance is zero regardless of the range") + { + float d = ink::runtime::internal::d_value(5, 5, range1, range1); + CHECK(d == 0); + d = ink::runtime::internal::d_value(5, 5, range1, range2); + CHECK(d == 0); + } } - GIVEN("Different Value") + + WHEN("the values are different") { float d1 = ink::runtime::internal::d_value(10, 20, range1, range1); float d2 = ink::runtime::internal::d_value(10, 20, range2, range2); float d3 = ink::runtime::internal::d_value(10, 20, range1, range2); - CHECK(d3 == 0); // there are both in the center of their respected range - CHECK(d1 > d2); // same absolute distance in bigger range is a smaller distance + + THEN("the same absolute distance in a bigger range results in a smaller relative distance") + { + CHECK(d3 == 0); // both values are at the centre of their respective ranges + CHECK(d1 > d2); + } } } - SECTION("Sets") + + GIVEN("two sets and a match array for containment distance comparison") { - GIVEN("Equal Sets") + WHEN("the sets are equal") { ink::size_t lh[] = {5, 10}; ink::size_t rh[] = {5, 10}; int matches[] = {0, 0, 0, 0, 0, 5, 6, 7, 8, 9}; float d = ink::runtime::internal::d_contains(lh, rh, matches); - CHECK_THAT(d, Catch::Matchers::WithinAbs(0, 0.001)); + + THEN("the distance is zero") { CHECK_THAT(d, Catch::Matchers::WithinAbs(0, 0.001)); } } - GIVEN("Dropped Values") + + WHEN("the right-hand set has dropped values") { ink::size_t lh[] = {5, 10}; ink::size_t rh[] = {5, 8}; int matches[] = {0, 0, 0, 0, 0, 5, -1, -1, 6, 7}; float d = ink::runtime::internal::d_contains(lh, rh, matches); - CHECK_THAT(d, Catch::Matchers::WithinAbs(0.4, 0.001)); + + THEN("the distance reflects the dropped elements") + { + CHECK_THAT(d, Catch::Matchers::WithinAbs(0.4, 0.001)); + } } - GIVEN("New Values") + + WHEN("the right-hand set has new values") { ink::size_t lh[] = {5, 8}; ink::size_t rh[] = {5, 10}; int matches[] = {0, 0, 0, 0, 0, 5, 6, 7}; float d = ink::runtime::internal::d_contains(lh, rh, matches); - CHECK_THAT(d, Catch::Matchers::WithinAbs(0.4, 0.001)); + + THEN("the distance reflects the new elements") + { + CHECK_THAT(d, Catch::Matchers::WithinAbs(0.4, 0.001)); + } } - GIVEN("Swapped Values") + + WHEN("the sets have swapped values") { ink::size_t lh[] = {5, 10}; ink::size_t rh[] = {5, 10}; int matches[] = {0, 0, 0, 0, 0, 5, 9, 6, 8, 7}; float d = ink::runtime::internal::d_contains(lh, rh, matches); - CHECK_THAT(d, Catch::Matchers::WithinAbs(0, 0.001)); + + THEN("the distance is zero since order does not matter") + { + CHECK_THAT(d, Catch::Matchers::WithinAbs(0, 0.001)); + } } - GIVEN("Changed Values") + + WHEN("the sets have changed values") { ink::size_t lh[] = {5, 10}; ink::size_t rh[] = {5, 10}; int matches[] = {0, 0, 0, 0, 0, 5, 9, -1, -1, -1}; float d = ink::runtime::internal::d_contains(lh, rh, matches); - CHECK_THAT(d, Catch::Matchers::WithinAbs(0.75, 0.001)); + + THEN("the distance reflects the changed elements") + { + CHECK_THAT(d, Catch::Matchers::WithinAbs(0.75, 0.001)); + } } } } -SCENARIO("find best assigments", "[list_match][hungarian]") +SCENARIO("find best assigments", "[list-matching][unit][internals]") { - GIVEN("Example 1") + GIVEN("a 3x3 cost matrix (example 1)") { // clang-format off float cost[] = { @@ -138,12 +188,17 @@ SCENARIO("find best assigments", "[list_match][hungarian]") // clang-format on int matches[3]; float total_cost = ink::algorithms::hungarian_solver(cost, matches, 3); - CHECK(total_cost == 15.f); - CHECK(matches[0] == 0); - CHECK(matches[1] == 2); - CHECK(matches[2] == 1); + + THEN("the optimal assignment has the correct total cost and match indices") + { + CHECK(total_cost == 15.f); + CHECK(matches[0] == 0); + CHECK(matches[1] == 2); + CHECK(matches[2] == 1); + } } - GIVEN("Example 2") + + GIVEN("a 3x3 cost matrix (example 2)") { // clang-format off float cost[] = { @@ -151,15 +206,21 @@ SCENARIO("find best assigments", "[list_match][hungarian]") 125 , 135/**/, 148 , 150/**/, 175 , 250 , }; - // clang-format off - int matches[3]; + // clang-format on + int matches[3]; float total_cost = ink::algorithms::hungarian_solver(cost, matches, 3); - CHECK(total_cost == 407); - CHECK(matches[0] == 2); - CHECK(matches[1] == 1); - CHECK(matches[2] == 0); + + THEN("the optimal assignment has the correct total cost and match indices") + { + CHECK(total_cost == 407); + CHECK(matches[0] == 2); + CHECK(matches[1] == 1); + CHECK(matches[2] == 0); + } } - GIVEN("With Example 1 Threshold") { + + GIVEN("a 3x3 cost matrix with a drop threshold applied (example 1 with threshold)") + { // clang-format off float cost[] = { 8/**/, 5 , 9 , @@ -169,42 +230,56 @@ SCENARIO("find best assigments", "[list_match][hungarian]") // clang-format on int matches[3]; float total_cost = ink::algorithms::hungarian_solver(cost, matches, 3, 5); - CHECK(total_cost == 15.f); - CHECK(matches[0] == -1); - CHECK(matches[1] == 2); - CHECK(matches[2] == 1); + + THEN("the first element is dropped and the remaining assignments are optimal") + { + CHECK(total_cost == 15.f); + CHECK(matches[0] == -1); + CHECK(matches[1] == 2); + CHECK(matches[2] == 1); + } } } -SCENARIO("Simple List Migration stories", "[list_match]") +SCENARIO("Simple List Migration stories", "[list-matching][migration][integration]") { - GIVEN("Splitted List") + GIVEN("a story split across two versions with list extensions and a typo fix") { std::unique_ptr ink_a{story::from_file(INK_TEST_RESOURCE_DIR "ListMatchStoryA.bin")}; std::unique_ptr ink_b{story::from_file(INK_TEST_RESOURCE_DIR "ListMatchStoryB.bin")}; globals globals_a = ink_a->new_globals(); runner thread_a = ink_a->new_runner(globals_a); - WHEN("Load new list extensions, split and typo fix") + + WHEN("the old story is advanced past the first choice and a snapshot is taken") { - CHECK(thread_a->getline() == "You are currently at Flor, Balcony\n"); + REQUIRE(thread_a->getline() == "You are currently at Flor, Balcony\n"); REQUIRE(thread_a->has_choices()); thread_a->choose(0); std::unique_ptr snap{thread_a->create_snapshot()}; REQUIRE(snap->can_be_migrated()); - CHECK( - thread_a->getall() - == "More\nYou are still at Flor, Balcony - all posibilities are Flor, Balcony, Kitchen, Garden\n" - ); - - auto globals_b = ink_b->new_globals_from_snapshot(*snap); + THEN("the old story continues with the original list names") + { + CHECK( + thread_a->getall() + == "More\nYou are still at Flor, Balcony - all posibilities are Flor, Balcony, Kitchen, Garden\n" + ); + } - auto thread_b = ink_b->new_runner_from_snapshot(*snap, globals_b); + AND_WHEN("the snapshot is loaded into the new story version") + { + auto globals_b = ink_b->new_globals_from_snapshot(*snap); + auto thread_b = ink_b->new_runner_from_snapshot(*snap, globals_b); + std::string out = thread_b->getall(); - CHECK( - thread_b->getall() - == "More\nYou are still at Floor, Balcony - all posibilities are Kitchen, Street, Floor, Balcony, Livingroom, Garden\n" - ); + THEN("the new story resumes with the corrected list names and extended possibilities") + { + CHECK( + out + == "More\nYou are still at Floor, Balcony - all posibilities are Kitchen, Street, Floor, Balcony, Livingroom, Garden\n" + ); + } + } } } } diff --git a/inkcpp_test/Lists.cpp b/inkcpp_test/Lists.cpp index d5c86526..4105a5ef 100644 --- a/inkcpp_test/Lists.cpp +++ b/inkcpp_test/Lists.cpp @@ -10,7 +10,7 @@ using namespace ink::runtime; -SCENARIO("List logic operations", "[lists]") +SCENARIO("List logic operations", "[lists][runtime]") { GIVEN("a demo story") { @@ -46,7 +46,7 @@ Hey } } -SCENARIO("run a story with lists", "[lists]") +SCENARIO("run a story with lists", "[lists][runtime]") { GIVEN("a story with multi lists") { diff --git a/inkcpp_test/LookaheadSafe.cpp b/inkcpp_test/LookaheadSafe.cpp index 1dd5c1db..f9a2f3bb 100644 --- a/inkcpp_test/LookaheadSafe.cpp +++ b/inkcpp_test/LookaheadSafe.cpp @@ -8,7 +8,7 @@ using namespace ink::runtime; -SCENARIO("a story with external functions and glue", "[external]") +SCENARIO("a story with external functions and glue", "[external-functions][glue][runtime]") { GIVEN("the story") { diff --git a/inkcpp_test/Migration.cpp b/inkcpp_test/Migration.cpp index 74549599..88ac0da1 100644 --- a/inkcpp_test/Migration.cpp +++ b/inkcpp_test/Migration.cpp @@ -11,223 +11,383 @@ using namespace ink::runtime; -SCENARIO("Simple isolated migration tests.", "[migration]") +SCENARIO("Simple isolated migration tests.", "[migration][integration]") { - std::unique_ptr base_story{story::from_file(INK_TEST_RESOURCE_DIR "MigrationBase.bin")}; - globals base_globals = base_story->new_globals(); - runner base_thread = base_story->new_runner(base_globals); - WHEN("Just Run the base story") + GIVEN("a loaded base story with a fresh runner") { - std::string content = base_thread->getall(); - REQUIRE(base_thread->has_choices()); - REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); - THEN("All values as defined") - { - CHECK(base_globals->get("do_not_migrate").value_or(0) == 10); - CHECK(base_globals->get("do_migrate").value_or(0) == 15); - } - REQUIRE(base_thread->num_global_tags() == 2); - REQUIRE(base_thread->get_global_tag(0) == std::string("test:migration")); - REQUIRE(base_thread->get_global_tag(1) == std::string("flavor:base")); - REQUIRE(base_thread->num_knot_tags() == 1); - REQUIRE(base_thread->get_knot_tag(0) == std::string("knot:Main")); - base_thread->choose(0); - REQUIRE(base_thread->getall() == "A\ncatch\n5 3\n1 -1 1\nOh.\n"); - } - GIVEN("Simple story with changes in globals.") - { - std::unique_ptr new_story{story::from_file(INK_TEST_RESOURCE_DIR - "MigrationChangeGlobals.bin")}; - WHEN("Just Run the new story") - { - globals new_globals = new_story->new_globals(); - runner new_thread = new_story->new_runner(new_globals); - std::string content = new_thread->getall(); - REQUIRE(new_thread->has_choices()); - REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); - THEN("All values as defined") - { - CHECK(new_globals->get("do_migrate").value_or(0) == 10); - CHECK(new_globals->get("new_var").value_or(0) == 20); - } - new_thread->choose(0); - REQUIRE(new_thread->getall() == "A\ncatch\n1 -1 1\nOh.\n"); - } - WHEN("Run base story and load in new_story") + std::unique_ptr base_story{story::from_file(INK_TEST_RESOURCE_DIR "MigrationBase.bin")}; + globals base_globals = base_story->new_globals(); + runner base_thread = base_story->new_runner(base_globals); + + WHEN("the base story is run") { std::string content = base_thread->getall(); - REQUIRE(base_thread->has_choices()); - REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); - base_thread->choose(0); - std::unique_ptr snap{base_thread->create_snapshot()}; - REQUIRE(snap->can_be_migrated()); - globals new_globals = new_story->new_globals_from_snapshot(*snap); - runner new_thread = new_story->new_runner_from_snapshot(*snap, new_globals); - THEN("expect merged globals") + + THEN("the intro is output and choices are available") { - CHECK_FALSE(new_globals->get("do_not_migrate").has_value()); - CHECK(new_globals->get("do_migrate").value_or(0) == 15); - CHECK(new_globals->get("new_var").value_or(0) == 20); + REQUIRE(base_thread->has_choices()); + REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); } - THEN("expect story to continue normally") + + THEN("initial global variable values are set correctly") { - content = new_thread->getall(); - REQUIRE(content == "A\ncatch\n1 -1 1\nOh.\n"); + CHECK(base_globals->get("do_not_migrate").value_or(0) == 10); + CHECK(base_globals->get("do_migrate").value_or(0) == 15); + } + + THEN("global and knot tags are correct") + { + REQUIRE(base_thread->num_global_tags() == 2); + REQUIRE(base_thread->get_global_tag(0) == std::string("test:migration")); + REQUIRE(base_thread->get_global_tag(1) == std::string("flavor:base")); + REQUIRE(base_thread->num_knot_tags() == 1); + REQUIRE(base_thread->get_knot_tag(0) == std::string("knot:Main")); + } + + AND_WHEN("the first choice is taken") + { + REQUIRE(base_thread->has_choices()); // guard + base_thread->choose(0); + + THEN("the story outputs the expected follow-up content") + { + REQUIRE(base_thread->getall() == "A\ncatch\n5 3\n1 -1 1\nOh.\n"); + } } } - } - GIVEN("Simple story with changed knots.") - { - std::unique_ptr new_story{story::from_file(INK_TEST_RESOURCE_DIR - "MigrationChangeNodes.bin")}; - WHEN("Just Run the new story") + + GIVEN("and a variant story with changed global variables") { - globals new_globals = new_story->new_globals(); - runner new_thread = new_story->new_runner(new_globals); - std::string content = new_thread->getall(); - REQUIRE(new_thread->has_choices()); - REQUIRE(content == "B\n-1 0 0\nThis is a simple story.\n"); - new_thread->choose(0); - content = new_thread->getall(); - REQUIRE(content == "A\ncatch\n-1 1 1\nOh.\n"); + std::unique_ptr new_story{story::from_file(INK_TEST_RESOURCE_DIR + "MigrationChangeGlobals.bin")}; + + WHEN("the new story is run fresh from the start") + { + globals new_globals = new_story->new_globals(); + runner new_thread = new_story->new_runner(new_globals); + std::string content = new_thread->getall(); + + THEN("the output matches the base story") + { + REQUIRE(new_thread->has_choices()); + REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); + } + + THEN("the updated global variable values are in effect") + { + CHECK(new_globals->get("do_migrate").value_or(0) == 10); + CHECK(new_globals->get("new_var").value_or(0) == 20); + } + + AND_WHEN("a choice is made") + { + REQUIRE(new_thread->has_choices()); // guard + new_thread->choose(0); + + THEN("the story continues with updated variable output") + { + REQUIRE(new_thread->getall() == "A\ncatch\n1 -1 1\nOh.\n"); + } + } + } + + WHEN("the base story is run to a checkpoint and migrated to the new story") + { + std::string content = base_thread->getall(); + REQUIRE(base_thread->has_choices()); + REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); + base_thread->choose(0); + std::unique_ptr snap{base_thread->create_snapshot()}; + REQUIRE(snap->can_be_migrated()); + globals new_globals = new_story->new_globals_from_snapshot(*snap); + runner new_thread = new_story->new_runner_from_snapshot(*snap, new_globals); + + THEN("removed variable is gone, migrated value is kept, new variable defaults") + { + CHECK_FALSE(new_globals->get("do_not_migrate").has_value()); + CHECK(new_globals->get("do_migrate").value_or(0) == 15); + CHECK(new_globals->get("new_var").value_or(0) == 20); + } + + THEN("the story continues normally from the migration point") + { + REQUIRE(new_thread->getall() == "A\ncatch\n1 -1 1\nOh.\n"); + } + } } - WHEN("Run base story and load new story.") + + GIVEN("and a variant story with changed knots") { - std::string content = base_thread->getall(); - REQUIRE(base_thread->has_choices()); - REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); - base_thread->choose(0); - std::unique_ptr snap{base_thread->create_snapshot()}; - REQUIRE(snap->can_be_migrated()); - globals new_globals = new_story->new_globals_from_snapshot(*snap); - runner new_thread = new_story->new_runner_from_snapshot(*snap, new_globals); - THEN("Migrated visit counts. Unreachable node has visit, new node has no") + std::unique_ptr new_story{story::from_file(INK_TEST_RESOURCE_DIR + "MigrationChangeNodes.bin")}; + + WHEN("the new story is run fresh from the start") { - content = new_thread->getall(); - REQUIRE(content == "A\ncatch\n1 1 1\nOh.\n"); + globals new_globals = new_story->new_globals(); + runner new_thread = new_story->new_runner(new_globals); + std::string content = new_thread->getall(); + + THEN("the new story's intro is shown with modified visit counts") + { + REQUIRE(new_thread->has_choices()); + REQUIRE(content == "B\n-1 0 0\nThis is a simple story.\n"); + } + + AND_WHEN("a choice is made") + { + REQUIRE(new_thread->has_choices()); // guard + new_thread->choose(0); + + THEN("the story continues with the new knot's output") + { + REQUIRE(new_thread->getall() == "A\ncatch\n-1 1 1\nOh.\n"); + } + } + } + + WHEN("the base story is run to a checkpoint and migrated to the new story") + { + std::string content = base_thread->getall(); + REQUIRE(base_thread->has_choices()); + REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); + base_thread->choose(0); + std::unique_ptr snap{base_thread->create_snapshot()}; + REQUIRE(snap->can_be_migrated()); + globals new_globals = new_story->new_globals_from_snapshot(*snap); + runner new_thread = new_story->new_runner_from_snapshot(*snap, new_globals); + + THEN("visit counts are migrated and the story continues with node-aware output") + { + REQUIRE(new_thread->getall() == "A\ncatch\n1 1 1\nOh.\n"); + } } } - } - GIVEN("Simple story with changed temporary variables.") - { - std::unique_ptr new_story{story::from_file(INK_TEST_RESOURCE_DIR "MigrationTemp.bin")}; - WHEN("Just Run the new story.") + + GIVEN("and a variant story with changed temporary variables") { - globals new_globals = new_story->new_globals(); - runner new_thread = new_story->new_runner(new_globals); - std::string content = new_thread->getall(); - REQUIRE(new_thread->has_choices()); - REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); - REQUIRE(new_thread->get_current_knot() == 0x25e83b84); - new_thread->choose(0); - REQUIRE(new_thread->get_current_knot() == 0x25e83b84); - content = new_thread->getall(); - REQUIRE(new_thread->get_current_knot() == 0x25e83b84); - REQUIRE(content == "A\ncatch\n2 - 3\n1 -1 1\nOh.\n"); + std::unique_ptr new_story{story::from_file(INK_TEST_RESOURCE_DIR "MigrationTemp.bin")}; + + WHEN("the new story is run fresh from the start") + { + globals new_globals = new_story->new_globals(); + runner new_thread = new_story->new_runner(new_globals); + std::string content = new_thread->getall(); + + THEN("the intro is shown at the expected knot") + { + REQUIRE(new_thread->has_choices()); + REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); + REQUIRE(new_thread->get_current_knot() == 0x25e83b84); + } + + AND_WHEN("a choice is made") + { + REQUIRE(new_thread->has_choices()); // guard + new_thread->choose(0); + std::string after = new_thread->getall(); + + THEN("the story continues with updated temporary variable output at the same knot") + { + REQUIRE(new_thread->get_current_knot() == 0x25e83b84); + REQUIRE(after == "A\ncatch\n2 - 3\n1 -1 1\nOh.\n"); + } + } + } + + WHEN("the base story is run to a checkpoint and migrated to the new story") + { + std::string content = base_thread->getall(); + REQUIRE(base_thread->has_choices()); + REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); + REQUIRE(base_thread->get_current_knot() == 0x25e83b84); + base_thread->choose(0); + REQUIRE(base_thread->get_current_knot() == 0x25e83b84); + std::unique_ptr snap{base_thread->create_snapshot()}; + REQUIRE(snap->can_be_migrated()); + globals new_globals = new_story->new_globals_from_snapshot(*snap); + runner new_thread = new_story->new_runner_from_snapshot(*snap, new_globals); + std::string after = new_thread->getall(); + + THEN("the old temporary variable is preserved and the new one uses its default") + { + REQUIRE(new_thread->get_current_knot() == 0x25e83b84); + REQUIRE(after == "A\ncatch\n5 - 6\n1 -1 1\nOh.\n"); + } + } } - WHEN("Run base story and load new story.") + + GIVEN("and a variant story with changed knot and global tags") { - std::string content = base_thread->getall(); - REQUIRE(base_thread->has_choices()); - REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); - REQUIRE(base_thread->get_current_knot() == 0x25e83b84); - base_thread->choose(0); - REQUIRE(base_thread->get_current_knot() == 0x25e83b84); - std::unique_ptr snap{base_thread->create_snapshot()}; - REQUIRE(snap->can_be_migrated()); - globals new_globals = new_story->new_globals_from_snapshot(*snap); - runner new_thread = new_story->new_runner_from_snapshot(*snap, new_globals); - THEN("Transfared old temporary variable, and kept default from new one.") + std::unique_ptr new_story{story::from_file(INK_TEST_RESOURCE_DIR + "MigrationKnotTags.bin")}; + + WHEN("the new story is run fresh from the start") { - REQUIRE(new_thread->get_current_knot() == 0x25e83b84); - content = new_thread->getall(); - REQUIRE(content == "A\ncatch\n5 - 6\n1 -1 1\nOh.\n"); + globals new_globals = new_story->new_globals(); + runner new_thread = new_story->new_runner(new_globals); + std::string content = new_thread->getall(); + + THEN("the new global and knot tags are present") + { + REQUIRE(new_thread->has_choices()); + REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); + REQUIRE(new_thread->num_global_tags() == 2); + REQUIRE(new_thread->get_global_tag(0) == std::string("test:migration")); + REQUIRE(new_thread->get_global_tag(1) == std::string("flavor:changed")); + REQUIRE(new_thread->num_knot_tags() == 1); + REQUIRE(new_thread->get_knot_tag(0) == std::string("knot:different")); + } + } + + WHEN("the base story is run to a checkpoint and migrated to the new story") + { + std::string content = base_thread->getall(); + REQUIRE(base_thread->has_choices()); + REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); + REQUIRE(base_thread->num_global_tags() == 2); + REQUIRE(base_thread->get_global_tag(0) == std::string("test:migration")); + REQUIRE(base_thread->get_global_tag(1) == std::string("flavor:base")); + REQUIRE(base_thread->num_knot_tags() == 1); + REQUIRE(base_thread->get_knot_tag(0) == std::string("knot:Main")); + base_thread->choose(0); + std::unique_ptr snap{base_thread->create_snapshot()}; + REQUIRE(snap->can_be_migrated()); + globals new_globals = new_story->new_globals_from_snapshot(*snap); + runner new_thread = new_story->new_runner_from_snapshot(*snap, new_globals); + + THEN("the new story's global and knot tags are reflected after migration") + { + REQUIRE(new_thread->num_global_tags() == 2); + REQUIRE(new_thread->get_global_tag(0) == std::string("test:migration")); + REQUIRE(new_thread->get_global_tag(1) == std::string("flavor:changed")); + REQUIRE(new_thread->num_knot_tags() == 1); + REQUIRE(new_thread->get_knot_tag(0) == std::string("knot:different")); + } + + THEN("the story continues normally from the migration point") + { + REQUIRE(new_thread->getall() == "A\ncatch\n5 3\n1 -1 1\nOh.\n"); + } } } } - GIVEN("Simple story with other knot/global tags.") +} + +SCENARIO("Migration Test for small story", "[migration][integration]") +{ + GIVEN("a 'before' and 'after' version of the same story are loaded") { - std::unique_ptr new_story{story::from_file(INK_TEST_RESOURCE_DIR "MigrationKnotTags.bin") - }; - WHEN("Just Run the new story.") - { - globals new_globals = new_story->new_globals(); - runner new_thread = new_story->new_runner(new_globals); - std::string content = new_thread->getall(); - REQUIRE(new_thread->has_choices()); - REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); - REQUIRE(new_thread->num_global_tags() == 2); - REQUIRE(new_thread->get_global_tag(0) == std::string("test:migration")); - REQUIRE(new_thread->get_global_tag(1) == std::string("flavor:changed")); - REQUIRE(new_thread->num_knot_tags() == 1); - REQUIRE(new_thread->get_knot_tag(0) == std::string("knot:different")); - } - WHEN("Run base story and load new story.") + std::unique_ptr before{story::from_file(INK_TEST_RESOURCE_DIR "MigrationBefore.bin")}; + std::unique_ptr after{story::from_file(INK_TEST_RESOURCE_DIR "MigrationAfter.bin")}; + + WHEN("the 'before' story is played through sand castle then swimming and a snapshot is taken") { - std::string content = base_thread->getall(); - REQUIRE(base_thread->has_choices()); - REQUIRE(content == "A\n0 -1 0\nThis is a simple story.\n"); - REQUIRE(base_thread->num_global_tags() == 2); - REQUIRE(base_thread->get_global_tag(0) == std::string("test:migration")); - REQUIRE(base_thread->get_global_tag(1) == std::string("flavor:base")); - REQUIRE(base_thread->num_knot_tags() == 1); - REQUIRE(base_thread->get_knot_tag(0) == std::string("knot:Main")); - base_thread->choose(0); - std::unique_ptr snap{base_thread->create_snapshot()}; + runner thread_before = before->new_runner(); + + // Advance through initial state + REQUIRE(thread_before->getall() == "We're going to the seaside!\n"); + REQUIRE(thread_before->num_choices() == 3); + CHECK(thread_before->get_choice(0)->text() == std::string("Make a sand castle")); + CHECK(thread_before->get_choice(1)->text() == std::string("Go swimming")); + CHECK(thread_before->get_choice(2)->text() == std::string("Time to go home")); + + thread_before->choose(0); // sand castle + REQUIRE( + thread_before->getall() + == "We made a great sand castle, it even has a moat!\nWe're going to the seaside!\nSo far " + "we've done the following: SandCastle\n" + ); + REQUIRE(thread_before->num_choices() == 3); + CHECK(thread_before->get_choice(0)->text() == std::string("Make a sand castle")); + CHECK(thread_before->get_choice(1)->text() == std::string("Go swimming")); + CHECK(thread_before->get_choice(2)->text() == std::string("Time to go home")); + + thread_before->choose(1); // swimming — snapshot point + std::unique_ptr snap{thread_before->create_snapshot()}; REQUIRE(snap->can_be_migrated()); - globals new_globals = new_story->new_globals_from_snapshot(*snap); - runner new_thread = new_story->new_runner_from_snapshot(*snap, new_globals); - THEN("Got new global/knot tags") + + THEN("the 'before' story continues from the swimming point with only the remaining choices") { - REQUIRE(new_thread->num_global_tags() == 2); - REQUIRE(new_thread->get_global_tag(0) == std::string("test:migration")); - REQUIRE(new_thread->get_global_tag(1) == std::string("flavor:changed")); - REQUIRE(new_thread->num_knot_tags() == 1); - REQUIRE(new_thread->get_knot_tag(0) == std::string("knot:different")); + REQUIRE( + thread_before->getall() + == "We swim and swam, it was delightful!\nWe're going to the seaside!\nSo far we've " + "done the following: Swimming, SandCastle\n" + ); + REQUIRE(thread_before->num_choices() == 2); + CHECK(thread_before->get_choice(0)->text() == std::string("Make a sand castle")); + CHECK(thread_before->get_choice(1)->text() == std::string("Time to go home")); } - THEN("continue the story normally") + + AND_WHEN("the snapshot is loaded into the 'after' story") { - REQUIRE(new_thread->getall() == "A\ncatch\n5 3\n1 -1 1\nOh.\n"); + runner thread_after = after->new_runner_from_snapshot(*snap); + std::string after_out = thread_after->getall(); + + THEN("the 'after' story resumes from the same point with the new Ice Cream choice") + { + REQUIRE( + after_out + == "We swim and swam, it was delightful!\nWe're going to the seaside!\nSo far " + "we've done the following: Swimming, SandCastle\n" + ); + REQUIRE(thread_after->num_choices() == 3); + CHECK(thread_after->get_choice(0)->text() == std::string("Make a sand castle")); + CHECK(thread_after->get_choice(1)->text() == std::string("Get Ice Cream")); + CHECK(thread_after->get_choice(2)->text() == std::string("Time to go home")); + } + + AND_WHEN("the Ice Cream choice is selected") + { + REQUIRE(thread_after->num_choices() == 3); // guard + thread_after->choose(1); + + THEN("the ice cream activity is shown with updated cumulative history") + { + REQUIRE( + thread_after->getall() + == "We got ice cream, mine was raspberry!\nWe're going to the seaside!\nSo far " + "we've done the following: Swimming, SandCastle, IceCream\n" + ); + } + } } } } } -SCENARIO("Migration Test for small story", "[migration]") +SCENARIO("Migratability of finished threads", "[migration][integration]") { - std::unique_ptr before{story::from_file(INK_TEST_RESOURCE_DIR "MigrationBefore.bin")}; - std::unique_ptr after{story::from_file(INK_TEST_RESOURCE_DIR "MigrationAfter.bin")}; - GIVEN("Test sequcen with multiple loads") + GIVEN("A story with multipl threads") { - runner thread_before = before->new_runner(); - REQUIRE(thread_before->getall() == "We're going to the seaside!\n"); - CHECK(thread_before->num_choices() == 3); - CHECK(thread_before->get_choice(0)->text() == std::string("Make a sand castle")); - CHECK(thread_before->get_choice(1)->text() == std::string("Go swimming")); - CHECK(thread_before->get_choice(2)->text() == std::string("Time to go home")); - thread_before->choose(0); - REQUIRE(thread_before->getall() == "We made a great sand castle, it even has a moat!\nWe're going to the seaside!\nSo far we've done the following: SandCastle\n"); - CHECK(thread_before->num_choices() == 3); - CHECK(thread_before->get_choice(0)->text() == std::string("Make a sand castle")); - CHECK(thread_before->get_choice(1)->text() == std::string("Go swimming")); - CHECK(thread_before->get_choice(2)->text() == std::string("Time to go home")); - - thread_before->choose(1); - std::unique_ptr snap1{thread_before->create_snapshot()}; - REQUIRE(snap1->can_be_migrated()); - REQUIRE(thread_before->getall() == "We swim and swam, it was delightful!\nWe're going to the seaside!\nSo far we've done the following: Swimming, SandCastle\n"); - - CHECK(thread_before->num_choices() == 2); - CHECK(thread_before->get_choice(0)->text() == std::string("Make a sand castle")); - CHECK(thread_before->get_choice(1)->text() == std::string("Time to go home")); - - runner thread_after = after->new_runner_from_snapshot(*snap1); - REQUIRE(thread_after->getall() == "We swim and swam, it was delightful!\nWe're going to the seaside!\nSo far we've done the following: Swimming, SandCastle\n"); - CHECK(thread_after->num_choices() == 3); - CHECK(thread_after->get_choice(0)->text() == std::string("Make a sand castle")); - CHECK(thread_after->get_choice(1)->text() == std::string("Get Ice Cream")); - CHECK(thread_after->get_choice(2)->text() == std::string("Time to go home")); - thread_after->choose(1); - REQUIRE(thread_after->getall() == "We got ice cream, mine was raspberry!\nWe're going to the seaside!\nSo far we've done the following: Swimming, SandCastle, IceCream\n"); + std::unique_ptr base_story{story::from_file(INK_TEST_RESOURCE_DIR "UE_example.bin")}; + runner base_thread = base_story->new_runner(); + + WHEN("running a empty thread") + { + base_thread->move_to("Wait"); + base_thread->getall(); + THEN("the story should be migratable") + { + std::unique_ptr snap{base_thread->create_snapshot()}; + REQUIRE(snap->can_be_migrated()); + } + } + + WHEN("running an non empty thread to the end") + { + base_thread->move_to("TPotions.TTalkWithAnimals"); + REQUIRE(base_thread->getall() == "A potion which allows the consumer to talk with a variaty of animals. Just make sure\nyour serroundings do not think you are crazy.\n"); + REQUIRE(base_thread->num_choices() == 2); + base_thread->choose(1); + REQUIRE( + base_thread->getall() == "A take a sip. The potion tastes like Hores, it is afull.\n" + ); + + THEN("the story should be migratable") + { + std::unique_ptr snap{base_thread->create_snapshot()}; + REQUIRE(snap->can_be_migrated()); + } + } } } diff --git a/inkcpp_test/MoveTo.cpp b/inkcpp_test/MoveTo.cpp index fa3c3f2b..3ce09274 100644 --- a/inkcpp_test/MoveTo.cpp +++ b/inkcpp_test/MoveTo.cpp @@ -8,7 +8,7 @@ using namespace ink::runtime; -SCENARIO("run a story, but jump around manually", "[move_to]") +SCENARIO("run a story, but jump around manually", "[move-to][runtime]") { GIVEN("a story with side talking points") { diff --git a/inkcpp_test/MultiRunner.cpp b/inkcpp_test/MultiRunner.cpp index 14197a33..2f8cebf4 100644 --- a/inkcpp_test/MultiRunner.cpp +++ b/inkcpp_test/MultiRunner.cpp @@ -14,7 +14,7 @@ using namespace ink::runtime; -SCENARIO("UE example story snapshot migratability") +SCENARIO("UE example story snapshot migratability", "[snapshot][migration][integration]") { std::unique_ptr base_story{story::from_file(INK_TEST_RESOURCE_DIR "UE_example.bin")}; @@ -72,7 +72,7 @@ SCENARIO("UE example story snapshot migratability") } } -SCENARIO("UE example story migration from v1 to v2") +SCENARIO("UE example story migration from v1 to v2", "[snapshot][migration][integration]") { std::unique_ptr base_story{story::from_file(INK_TEST_RESOURCE_DIR "UE_example.bin")}; std::unique_ptr story_v2{story::from_file(INK_TEST_RESOURCE_DIR "UE_example_v2.bin")}; diff --git a/inkcpp_test/NewLines.cpp b/inkcpp_test/NewLines.cpp index fb3664a6..1040db12 100644 --- a/inkcpp_test/NewLines.cpp +++ b/inkcpp_test/NewLines.cpp @@ -7,62 +7,92 @@ using namespace ink::runtime; -std::unique_ptr lines_ink{story::from_file(INK_TEST_RESOURCE_DIR "LinesStory.bin")}; -runner lines_thread = lines_ink->new_runner(); - -SCENARIO("a story has the proper line breaks", "[lines]") +SCENARIO("a story has the proper line breaks", "[output][runtime]") { GIVEN("a story with line breaks") { - WHEN("starting thread") + std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "LinesStory.bin")}; + runner thread = ink->new_runner(); + + WHEN("the thread starts") + { + THEN("the thread can continue") { REQUIRE(thread->can_continue()); } + } + + WHEN("four lines are consumed in order") { - THEN("thread can continue") { REQUIRE(lines_thread->can_continue()); } - THEN("consume lines") + std::string line1 = thread->getline(); + std::string line2 = thread->getline(); + std::string line3 = thread->getline(); + std::string line4 = thread->getline(); + + THEN("each line matches the expected content") { - CHECK(lines_thread->getline() == "Line 1\n"); - CHECK(lines_thread->getline() == "Line 2\n"); - CHECK(lines_thread->getline() == "Line 3\n"); - CHECK(lines_thread->getline() == "Line 4\n"); + CHECK(line1 == "Line 1\n"); + CHECK(line2 == "Line 2\n"); + CHECK(line3 == "Line 3\n"); + CHECK(line4 == "Line 4\n"); } } - WHEN("running functions") + + WHEN("the runner jumps to the Functions knot") { - lines_thread->move_to(ink::hash_string("Functions")); - CHECK(lines_thread->getline() == "Function Line\n"); + thread->move_to(ink::hash_string("Functions")); + std::string line1 = thread->getline(); + std::string line2 = thread->getline(); - THEN("consume function result") { CHECK(lines_thread->getline() == "Function Result\n"); } + THEN("the function line and its result are output correctly") + { + CHECK(line1 == "Function Line\n"); + CHECK(line2 == "Function Result\n"); + } } - WHEN("consuming lines with tunnels") + + WHEN("the runner jumps to the Tunnels knot") { - lines_thread->move_to(ink::hash_string("Tunnels")); + thread->move_to(ink::hash_string("Tunnels")); + std::string line1 = thread->getline(); + std::string line2 = thread->getline(); + std::string line3 = thread->getline(); - THEN("tunnel lines are correct") + THEN("the tunnel lines are correct and the story ends") { - CHECK(lines_thread->getline() == "Tunnel Line\n"); - CHECK(lines_thread->getline() == "Tunnel Result\n"); - CHECK(lines_thread->getline() == ""); - CHECK_FALSE(lines_thread->can_continue()); + CHECK(line1 == "Tunnel Line\n"); + CHECK(line2 == "Tunnel Result\n"); + CHECK(line3 == ""); + CHECK_FALSE(thread->can_continue()); } } - WHEN("ignoring functions when applying glue") + + WHEN("the runner jumps to the ignore_functions_when_applying_glue knot") { - lines_thread->move_to(ink::hash_string("ignore_functions_when_applying_glue")); - CHECK(lines_thread->getline() == "\"I don't see why,\" I reply.\n"); + thread->move_to(ink::hash_string("ignore_functions_when_applying_glue")); + std::string line = thread->getline(); + + THEN("functions are correctly ignored when applying glue") + { + CHECK(line == "\"I don't see why,\" I reply.\n"); + } } } + GIVEN("a complex story") { std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "TheIntercept.bin")}; runner thread = ink->new_runner(); - // based on issue #82 - WHEN("run sequence 1 3 3 3 2 3") + + WHEN("the sequence 1 3 3 3 2 3 is chosen") { for (int i : {1, 3, 3, 3, 2, 3}) { thread->getall(); thread->choose(i - 1); } std::string text = thread->getall(); - THEN("no newline before dot") { REQUIRE(text == "\"I don't see why,\" I reply.\n"); } + + THEN("there is no spurious newline before the final punctuation") + { + REQUIRE(text == "\"I don't see why,\" I reply.\n"); + } } } } diff --git a/inkcpp_test/NoEarlyTags.cpp b/inkcpp_test/NoEarlyTags.cpp index 16ab40f3..fff8cc6c 100644 --- a/inkcpp_test/NoEarlyTags.cpp +++ b/inkcpp_test/NoEarlyTags.cpp @@ -7,34 +7,52 @@ using namespace ink::runtime; -std::unique_ptr tg_ink{story::from_file(INK_TEST_RESOURCE_DIR "NoEarlyTags.bin")}; -auto tg_thread = tg_ink->new_runner(); - -SCENARIO("Story with tags and glues", "[tags][glue]") +SCENARIO("Story with tags and glues", "[tags][glue][runtime]") { GIVEN("lines intersected with tags and glue") { - WHEN("no glue") + std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "NoEarlyTags.bin")}; + auto thread = ink->new_runner(); + + WHEN("the first line is read") { - CHECK(tg_thread->getline() == "Hey there, nice to meet you!\n"); - REQUIRE(tg_thread->num_tags() == 2); - CHECK(std::string(tg_thread->get_tag(0)) == "name fae03_name"); - CHECK(std::string(tg_thread->get_tag(1)) == "bb CaoimheGenericProgress"); + std::string line = thread->getline(); + + THEN("the output is correct and two tags are present") + { + CHECK(line == "Hey there, nice to meet you!\n"); + REQUIRE(thread->num_tags() == 2); + CHECK(std::string(thread->get_tag(0)) == "name fae03_name"); + CHECK(std::string(thread->get_tag(1)) == "bb CaoimheGenericProgress"); + } } - WHEN("next line") + + WHEN("the second line is read") { - CHECK(tg_thread->getline() == "Hey, I'm Hey and this is YOU, nice to meet you too!\n"); - REQUIRE(tg_thread->num_tags() == 1); - CHECK(std::string(tg_thread->get_tag(0)) == "name fae00_name"); + thread->getline(); // skip line 1 + std::string line = thread->getline(); + + THEN("the output is correct and one tag is present") + { + CHECK(line == "Hey, I'm Hey and this is YOU, nice to meet you too!\n"); + REQUIRE(thread->num_tags() == 1); + CHECK(std::string(thread->get_tag(0)) == "name fae00_name"); + } } - WHEN("glue stops tags lookahead") + + WHEN("the third line is read") { - CHECK( - tg_thread->getline() == "I'm Do! Most people can't pronounce it, just think 'Kee-vah\".\n" - ); - REQUIRE(tg_thread->num_tags() == 2); - CHECK(std::string(tg_thread->get_tag(0)) == "name fae03_name"); - CHECK(std::string(tg_thread->get_tag(1)) == "meet-character 5"); + thread->getline(); // skip line 1 + thread->getline(); // skip line 2 + std::string line = thread->getline(); + + THEN("glue stops tags lookahead and two tags are present") + { + CHECK(line == "I'm Do! Most people can't pronounce it, just think 'Kee-vah\".\n"); + REQUIRE(thread->num_tags() == 2); + CHECK(std::string(thread->get_tag(0)) == "name fae03_name"); + CHECK(std::string(thread->get_tag(1)) == "meet-character 5"); + } } } } diff --git a/inkcpp_test/Observer.cpp b/inkcpp_test/Observer.cpp index 234bb817..91cd117c 100644 --- a/inkcpp_test/Observer.cpp +++ b/inkcpp_test/Observer.cpp @@ -10,7 +10,7 @@ using namespace ink::runtime; -SCENARIO("Observer", "[variables][observer]") +SCENARIO("Observer", "[observer][globals][runtime]") { GIVEN("a story which changes variables") { @@ -21,39 +21,49 @@ SCENARIO("Observer", "[variables][observer]") std::stringstream debug; thread->set_debug_enabled(&debug); - WHEN("Run without observers") + WHEN("the story runs with no observers registered") { - CHECK(thread->getall() == "hello line 1 1 hello line 2 5 test line 3 5\n"); + std::string out = thread->getall(); + + THEN("the output is correct") + { + CHECK(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); + } } - WHEN("Run with observers read only, with specific type") + + WHEN("typed observers are registered for var1 and var2") { int var1_cnt = 0; auto var1 = [&var1_cnt](int32_t i) { if (var1_cnt++ == 0) { - CHECK(i == 1); - } else { - CHECK(i == 5); - } + CHECK(i == 1); + } else { + CHECK(i == 5); + } }; int var2_cnt = 0; auto var2 = [&var2_cnt](const char* s) { std::string str(s); if (var2_cnt++ == 0) { - CHECK(str == "hello"); - } else { - CHECK(str == "test"); - } + CHECK(str == "hello"); + } else { + CHECK(str == "test"); + } }; globals->observe("var1", var1); globals->observe("var2", var2); std::string out = thread->getall(); - CHECK(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); - CHECK(var1_cnt == 2); - CHECK(var2_cnt == 2); + THEN("the output is correct and each observer is called the expected number of times") + { + CHECK(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); + CHECK(var1_cnt == 2); + CHECK(var2_cnt == 2); + } } - WHEN("Run with generic observer") + + WHEN("generic value observers are registered for var1 and var2") { int var1_cnt = 0; auto var1 = [&var1_cnt](value v) { @@ -79,34 +89,47 @@ SCENARIO("Observer", "[variables][observer]") globals->observe("var2", var2); std::string out = thread->getall(); - CHECK(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); - CHECK(var1_cnt == 2); - CHECK(var2_cnt == 2); + THEN("the output is correct and each observer is called the expected number of times") + { + CHECK(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); + CHECK(var1_cnt == 2); + CHECK(var2_cnt == 2); + } } - WHEN("Bind multiple observer to same variables") + + WHEN("the same observer is bound twice to the same variable") { int var1_cnt = 0; auto var1 = [&var1_cnt](int32_t i) { if (var1_cnt++ < 2) { - CHECK(i == 1); - } else { - CHECK(i == 5); - } + CHECK(i == 1); + } else { + CHECK(i == 5); + } }; globals->observe("var1", var1); globals->observe("var1", var1); std::string out = thread->getall(); - CHECK(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); - CHECK(var1_cnt == 4); + THEN("the observer is called twice per change and the output is correct") + { + CHECK(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); + CHECK(var1_cnt == 4); + } } - WHEN("Run with missmatching type") + + WHEN("an observer with a mismatching type is registered") { auto var1 = [](uint32_t) { }; - CHECK_THROWS_AS(globals->observe("var1", var1), ink::ink_exception); + + THEN("registering the observer throws an exception") + { + CHECK_THROWS_AS(globals->observe("var1", var1), ink::ink_exception); + } } - WHEN("Just get pinged") + + WHEN("a no-argument ping observer is registered for var1") { int var1_cnt = 0; auto var1 = [&var1_cnt]() { @@ -115,23 +138,26 @@ SCENARIO("Observer", "[variables][observer]") globals->observe("var1", var1); std::string out = thread->getall(); - CHECK(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); - CHECK(var1_cnt == 2); + THEN("the output is correct and the observer is pinged once per change") + { + CHECK(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); + CHECK(var1_cnt == 2); + } } - WHEN("call with new and old value") + + WHEN("observers receiving both new and old values are registered") { int var1_cnt = 0; auto var1 = [&var1_cnt](int32_t i, ink::optional o_i) { if (var1_cnt++ == 0) { - CHECK(i == 1); - CHECK_FALSE(o_i.has_value()); - } else { - CHECK(i == 5); - CHECK(o_i.has_value()); - CHECK(o_i.value() == 1); - } + CHECK(i == 1); + CHECK_FALSE(o_i.has_value()); + } else { + CHECK(i == 5); + CHECK(o_i.has_value()); + CHECK(o_i.value() == 1); + } }; - int var2_cnt = 0; auto var2 = [&var2_cnt](value v, ink::optional o_v) { CHECK(v.type == value::Type::String); @@ -152,86 +178,105 @@ SCENARIO("Observer", "[variables][observer]") globals->observe("var2", var2); std::string out = thread->getall(); - CHECK(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); - CHECK(var1_cnt == 2); - CHECK(var2_cnt == 2); + THEN( + "each observer receives the correct new and old values and is called the expected number " + "of times" + ) + { + CHECK(out == "hello line 1 1 hello line 2 5 test line 3 5\n"); + CHECK(var1_cnt == 2); + CHECK(var2_cnt == 2); + } } - WHEN("Changing Same value at runtime") + + WHEN("an observer modifies the same variable it is observing at runtime") { int var1_cnt = 0; auto var1 = [&var1_cnt, &globals](int32_t i) { ++var1_cnt; if (var1_cnt == 1) { - CHECK(i == 1); - } else if (var1_cnt == 2) { - CHECK(i == 5); - globals->set("var1", 8); - } else if (var1_cnt == 3) { - CHECK(i == 8); - } + CHECK(i == 1); + } else if (var1_cnt == 2) { + CHECK(i == 5); + globals->set("var1", 8); + } else if (var1_cnt == 3) { + CHECK(i == 8); + } }; globals->observe("var1", var1); std::string out = thread->getall(); - CHECK(8 == globals->get("var1").value()); - CHECK(out == "hello line 1 1 hello line 2 8 test line 3 8\n"); - CHECK(var1_cnt == 3); + THEN("the modification is reflected in the output and the observer is called an extra time") + { + CHECK(globals->get("var1").value() == 8); + CHECK(out == "hello line 1 1 hello line 2 8 test line 3 8\n"); + CHECK(var1_cnt == 3); + } } - WHEN("Changing Sam value at bind time") + + WHEN("an observer modifies the same variable it is observing at bind time") { int var1_cnt = 0; auto var1 = [&var1_cnt, &globals](int32_t i) { ++var1_cnt; if (var1_cnt == 1) { - CHECK(i == 1); - globals->set("var1", 8); - } else if (var1_cnt == 2) { - CHECK(i == 8); - } else if (var1_cnt == 3) { - CHECK(i == 5); - } + CHECK(i == 1); + globals->set("var1", 8); + } else if (var1_cnt == 2) { + CHECK(i == 8); + } else if (var1_cnt == 3) { + CHECK(i == 5); + } }; globals->observe("var1", var1); std::string out = thread->getall(); - CHECK(5 == globals->get("var1").value()); - CHECK(out == "hello line 1 8 hello line 2 5 test line 3 5\n"); - CHECK(var1_cnt == 3); + THEN("the bind-time modification propagates and the story uses the modified value first") + { + CHECK(globals->get("var1").value() == 5); + CHECK(out == "hello line 1 8 hello line 2 5 test line 3 5\n"); + CHECK(var1_cnt == 3); + } } - WHEN("Changing Same value multiple times") + + WHEN("an observer modifies the same variable multiple times in a chain") { int var1_cnt = 0; auto var1 = [&var1_cnt, &globals](int32_t i) { ++var1_cnt; if (var1_cnt == 1) { - CHECK(i == 1); - globals->set("var1", 8); - } else if (var1_cnt == 2) { - CHECK(i == 8); - globals->set("var1", 10); - } else if (var1_cnt == 3) { - CHECK(i == 10); - } else if (var1_cnt == 4) { - CHECK(i == 5); - } + CHECK(i == 1); + globals->set("var1", 8); + } else if (var1_cnt == 2) { + CHECK(i == 8); + globals->set("var1", 10); + } else if (var1_cnt == 3) { + CHECK(i == 10); + } else if (var1_cnt == 4) { + CHECK(i == 5); + } }; globals->observe("var1", var1); std::string out = thread->getall(); - CHECK(5 == globals->get("var1").value()); - CHECK(out == "hello line 1 10 hello line 2 5 test line 3 5\n"); - CHECK(var1_cnt == 4); + THEN("each chained modification triggers the observer and the story reflects the final value") + { + CHECK(globals->get("var1").value() == 5); + CHECK(out == "hello line 1 10 hello line 2 5 test line 3 5\n"); + CHECK(var1_cnt == 4); + } } - WHEN("Changing Other value") + + WHEN("an observer for var1 modifies a different variable var2") { int var1_cnt = 0; auto var1 = [&var1_cnt, &globals](int32_t i) { if (var1_cnt++ == 0) { - CHECK(i == 1); - } else { - CHECK(i == 5); - globals->set("var2", "didum"); - } + CHECK(i == 1); + } else { + CHECK(i == 5); + globals->set("var2", "didum"); + } }; int var2_cnt = 0; auto var2 = [&var2_cnt]() { @@ -242,9 +287,15 @@ SCENARIO("Observer", "[variables][observer]") globals->observe("var2", var2); std::string out = thread->getall(); - CHECK(out == "hello line 1 1 didum line 2 5 test line 3 5\n"); - CHECK(var1_cnt == 2); - CHECK(var2_cnt == 3); + THEN( + "the cross-variable modification is reflected in the output and var2's observer is " + "triggered the extra time" + ) + { + CHECK(out == "hello line 1 1 didum line 2 5 test line 3 5\n"); + CHECK(var1_cnt == 2); + CHECK(var2_cnt == 3); + } } } } diff --git a/inkcpp_test/Pointer.cpp b/inkcpp_test/Pointer.cpp index 65878d2d..62a5c62d 100644 --- a/inkcpp_test/Pointer.cpp +++ b/inkcpp_test/Pointer.cpp @@ -9,6 +9,7 @@ class test_object { public: test_object() { num_objects++; } + ~test_object() { num_objects--; } int myData = 5; @@ -20,18 +21,18 @@ int test_object::num_objects = 0; typedef story_ptr test_object_p; -SCENARIO("pointers can be created and destroyed", "[pointer]") +SCENARIO("pointers can be created and destroyed", "[pointer][unit][internals]") { // ref_block normally held by the story - ref_block* story = new ref_block(); + ref_block* story = new ref_block(); story->references = 1; GIVEN("a story pointer") { { // create object and pointer - test_object* myObject = new test_object(); - test_object_p pointer = test_object_p(myObject, story); + test_object* myObject = new test_object(); + test_object_p pointer = test_object_p(myObject, story); THEN("it should be valid") { @@ -48,15 +49,9 @@ SCENARIO("pointers can be created and destroyed", "[pointer]") WHEN("it goes out of scope") { - THEN("the object should be destroyed") - { - REQUIRE(test_object::num_objects == 0); - } + THEN("the object should be destroyed") { REQUIRE(test_object::num_objects == 0); } - THEN("the story references should decrease") - { - REQUIRE(story->references == 1); - } + THEN("the story references should decrease") { REQUIRE(story->references == 1); } } } @@ -64,35 +59,29 @@ SCENARIO("pointers can be created and destroyed", "[pointer]") story = nullptr; } -SCENARIO("pointers can be copied and assigned", "[pointer]") +SCENARIO("pointers can be copied and assigned", "[pointer][unit][internals]") { // ref_block normally held by the story - ref_block* story = new ref_block(); + ref_block* story = new ref_block(); story->references = 1; GIVEN("a pointer") { // create object and pointer - test_object* myObject = new test_object(); - test_object_p pointer = test_object_p(myObject, story); + test_object* myObject = new test_object(); + test_object_p pointer = test_object_p(myObject, story); WHEN("we create a copy") { { test_object_p ref = pointer; - THEN("there should still only be one object") - { - REQUIRE(test_object::num_objects == 1); - } + THEN("there should still only be one object") { REQUIRE(test_object::num_objects == 1); } } WHEN("that copy goes out of scope") { - THEN("we should still have one object") - { - REQUIRE(test_object::num_objects == 1); - } + THEN("we should still have one object") { REQUIRE(test_object::num_objects == 1); } } } } @@ -101,17 +90,14 @@ SCENARIO("pointers can be copied and assigned", "[pointer]") { { // create object and two pointers - test_object* myObject = new test_object(); - test_object_p pointer = test_object_p(myObject, story); - test_object_p ref = pointer; + test_object* myObject = new test_object(); + test_object_p pointer = test_object_p(myObject, story); + test_object_p ref = pointer; } WHEN("both go out of scope") { - THEN("we should have zero instances") - { - REQUIRE(test_object::num_objects == 0); - } + THEN("we should have zero instances") { REQUIRE(test_object::num_objects == 0); } } } @@ -119,40 +105,31 @@ SCENARIO("pointers can be copied and assigned", "[pointer]") { { // create object and two pointers - test_object* myObject = new test_object(); - test_object_p pointer = test_object_p(myObject, story); - test_object_p ref = test_object_p(pointer); + test_object* myObject = new test_object(); + test_object_p pointer = test_object_p(myObject, story); + test_object_p ref = test_object_p(pointer); } WHEN("both go out of scope") { - THEN("we should have zero instances") - { - REQUIRE(test_object::num_objects == 0); - } + THEN("we should have zero instances") { REQUIRE(test_object::num_objects == 0); } } } GIVEN("two pointers to different objects") { - test_object* myObject = new test_object(); - test_object* myOtherObject = new test_object(); - test_object_p pointerA = test_object_p(myObject, story); - test_object_p pointerB = test_object_p(myOtherObject, story); + test_object* myObject = new test_object(); + test_object* myOtherObject = new test_object(); + test_object_p pointerA = test_object_p(myObject, story); + test_object_p pointerB = test_object_p(myOtherObject, story); - THEN("we should have two instances") - { - REQUIRE(test_object::num_objects == 2); - } + THEN("we should have two instances") { REQUIRE(test_object::num_objects == 2); } WHEN("we assign one pointer to the other") { pointerB = pointerA; - THEN("we should only have one object") - { - REQUIRE(test_object::num_objects == 1); - } + THEN("we should only have one object") { REQUIRE(test_object::num_objects == 1); } } } @@ -160,32 +137,26 @@ SCENARIO("pointers can be copied and assigned", "[pointer]") story = nullptr; } -SCENARIO("pointers become invalid when the story dies", "[pointer]") +SCENARIO("pointers become invalid when the story dies", "[pointer][unit][internals]") { // ref_block normally held by the story - ref_block* story = new ref_block(); + ref_block* story = new ref_block(); story->references = 1; GIVEN("a pointer") { - test_object* myObject = new test_object(); - test_object_p pointer = test_object_p(myObject, story); + test_object* myObject = new test_object(); + test_object_p pointer = test_object_p(myObject, story); - THEN("it should be valid") - { - REQUIRE(pointer); - } + THEN("it should be valid") { REQUIRE(pointer); } WHEN("the story becomes invalid") { story->valid = false; - THEN("the pointer should be invalid") - { - REQUIRE(!pointer); - } + THEN("the pointer should be invalid") { REQUIRE(! pointer); } } } delete story; -} \ No newline at end of file +} diff --git a/inkcpp_test/README.md b/inkcpp_test/README.md new file mode 100644 index 00000000..204cbcbc --- /dev/null +++ b/inkcpp_test/README.md @@ -0,0 +1,101 @@ +# inkcpp_test + +Catch2-based test suite for inkcpp. Tests are written in BDD style using +`SCENARIO` / `GIVEN` / `WHEN` / `AND_WHEN` / `THEN` / `AND_THEN` macros. + +## Running tests + +```sh +# inside the build directory +# Run all tests +ctest -C Release + +# Run test without bindings +ctest -V -C Release -R UnitTests +# or +inkcpp_test\Release\inkcpp_test.exe +# or inkcpp_test\Debug\inkcpp_test.exe +# or inkcpp_test\inkcpp_test.exe +# dependent on your build system + + +# Run a single test by scenario name +inkcpp_test.exe "Scenario: UE example story snapshot migratability" + +# Run all tests matching a tag expression +inkcpp_test.exe "[migration]" +inkcpp_test.exe "[regression]" +inkcpp_test.exe "[migration][integration]" +``` + +## Tag scheme + +Every `SCENARIO` is tagged along three independent dimensions. Tags compose +freely, so you can narrow a run to any intersection (e.g. `[regression][runtime]` +re-runs only runtime regression tests). + +| Dimension | Purpose | Values | +|---------------|------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Feature** | What capability is under test | `[array]`, `[callstack]`, `[choices]`, `[compiler]`, `[external-functions]`, `[globals]`, `[glue]`, `[labels]`, `[list-matching]`, `[lists]`, `[migration]`, `[move-to]`, `[observer]`, `[output]`, `[pointer]`, `[restorable]`, `[snapshot]`, `[stack]`, `[tags]`, `[utf-8]`, `[values]` | +| **Component** | Which subsystem owns the behaviour | `[compiler]`, `[internals]`, `[runtime]` | +| **Type** | What kind of test it is | `[integration]`, `[regression]`, `[unit]` | + +### Feature tags + +| Tag | What it covers | +|------------------------|-----------------------------------------------------------------| +| `[array]` | `allocated_restorable_array` internals | +| `[callstack]` | Thread forking and collapse on the internal callstack | +| `[choices]` | Choice presentation, selection, and bracketed choice edge cases | +| `[compiler]` | Ink JSON → binary compilation | +| `[external-functions]` | Binding and calling external (C++) functions from Ink | +| `[globals]` | Global variable get/set and observer callbacks | +| `[glue]` | Glue (`<>`) joining lines across knots and functions | +| `[labels]` | Label-based conditional choices | +| `[list-matching]` | Hungarian solver and list similarity distance functions | +| `[lists]` | Ink list creation, modification, and iteration | +| `[migration]` | Snapshot migration between story versions | +| `[move-to]` | `runner::move_to()` — jumping to named knots at runtime | +| `[observer]` | Variable observer callbacks (typed, generic, ping, new/old) | +| `[output]` | Line output formatting, newlines, whitespace, and glue | +| `[pointer]` | `story_ptr` reference-counted pointer internals | +| `[regression]` | Tests added to prevent a specific filed bug from recurring | +| `[restorable]` | `restorable` collection save/restore/forget | +| `[snapshot]` | Snapshot creation, serialisation, and restoration | +| `[stack]` | `simple_restorable_stack` push/pop/save/restore | +| `[tags]` | Ink tag reading (global, knot, inline, choice tags) | +| `[utf-8]` | Multi-byte character handling throughout the pipeline | +| `[values]` | Internal `value` type arithmetic and equality | + +### Component tags + +| Tag | What it covers | +|---------------|-----------------------------------------------------------------------| +| `[compiler]` | `ink::compiler` — Ink JSON → inkcpp binary | +| `[internals]` | `ink::runtime::internal` — data structures not part of the public API | +| `[runtime]` | `ink::runtime` — story, runner, globals public API | + +### Type tags + +| Tag | What it covers | +|-----------------|---------------------------------------------------------------------------------------| +| `[integration]` | Tests that exercise multiple components together (e.g. compiler + runtime + snapshot) | +| `[regression]` | Tests tied to a specific GitHub issue, named `_ #NNN` in the scenario | +| `[unit]` | Tests of a single class or function in isolation, usually with no story file | + +## BDD conventions + +- **`GIVEN`** — sets up the state of the world (story loaded, globals created, etc.). + Contains no assertions. +- **`WHEN`** — performs a single action (run a line, make a choice, take a snapshot). + Contains no assertions. +- **`AND_WHEN`** — a dependent action that follows a prior `WHEN`. Placed *inside* the + `WHEN` it depends on. +- **`THEN`** — asserts one observable outcome. Contains no story-advancing calls. +- **`AND_THEN`** — an additional assertion that belongs to the same observable moment. + +Multiple independent `WHEN` blocks inside one `GIVEN` are run as separate test +paths — Catch2 re-enters the `GIVEN` once per `WHEN`. + +For further reference see [`test-cases-and-sections.md`](https://github.com/catchorg/Catch2/blob/devel/docs/test-cases-and-sections.md) +in the repository root. diff --git a/inkcpp_test/Restorable.cpp b/inkcpp_test/Restorable.cpp index 5cb43c6c..7d43ea39 100644 --- a/inkcpp_test/Restorable.cpp +++ b/inkcpp_test/Restorable.cpp @@ -4,7 +4,7 @@ using ink::runtime::internal::restorable; -SCENARIO("a restorable collection can operate like a stack", "[restorable]") +SCENARIO("a restorable collection can operate like a stack", "[restorable][unit][internals]") { GIVEN("an empty restorable collection") { @@ -120,7 +120,10 @@ void ForgetAndVerifyStack( } } -SCENARIO("a collection can be restored no matter how many times you push or pop", "[restorable]") +SCENARIO( + "a collection can be restored no matter how many times you push or pop", + "[restorable][unit][internals]" +) { // Create the collection constexpr size_t size = 128; @@ -186,7 +189,7 @@ SCENARIO("a collection can be restored no matter how many times you push or pop" } } -SCENARIO("saving does not disrupt iteration", "[restorable]") +SCENARIO("saving does not disrupt iteration", "[restorable][unit][internals]") { // Create the collection constexpr size_t size = 128; @@ -271,7 +274,7 @@ SCENARIO("saving does not disrupt iteration", "[restorable]") } } -SCENARIO("save points can be forgotten", "[restorable]") +SCENARIO("save points can be forgotten", "[restorable][unit][internals]") { // Create the collection constexpr size_t size = 128; diff --git a/inkcpp_test/SpaceAfterBracketChoice.cpp b/inkcpp_test/SpaceAfterBracketChoice.cpp index c4e692c6..9a908d35 100644 --- a/inkcpp_test/SpaceAfterBracketChoice.cpp +++ b/inkcpp_test/SpaceAfterBracketChoice.cpp @@ -7,35 +7,35 @@ using namespace ink::runtime; -SCENARIO("a story with bracketed choices and spaces can choose correctly", "[choices]") +SCENARIO("a story with bracketed choices and spaces can choose correctly", "[choices][runtime]") { - GIVEN("a story with line breaks") + GIVEN("a story with bracketed choices") { std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "ChoiceBracketStory.bin")}; runner thread = ink->new_runner(); thread->getall(); - WHEN("start thread") + + WHEN("the story is at the first choice point") { - THEN("thread has choices") - { - thread->getall(); - REQUIRE(thread->has_choices()); - } - WHEN("choose choice 1") + thread->getall(); + + THEN("choices are available") { REQUIRE(thread->has_choices()); } + + AND_WHEN("choice 1 is made") { thread->choose(0); thread->getall(); - THEN("still has choices") - { - thread->getall(); - REQUIRE(thread->has_choices()); - } + thread->getall(); + + THEN("choices are still available after choice 1") { REQUIRE(thread->has_choices()); } } - WHEN("choose choice 2") + + AND_WHEN("choice 2 is made") { thread->choose(1); thread->getall(); - THEN("still has choices") { REQUIRE(thread->has_choices()); } + + THEN("choices are still available after choice 2") { REQUIRE(thread->has_choices()); } } } } diff --git a/inkcpp_test/Stack.cpp b/inkcpp_test/Stack.cpp index e0bc8c22..da78413b 100644 --- a/inkcpp_test/Stack.cpp +++ b/inkcpp_test/Stack.cpp @@ -5,14 +5,14 @@ using ink::runtime::internal::simple_restorable_stack; template -using fixed_restorable_stack = ink::runtime::internal::managed_restorable_stack; +using fixed_restorable_stack = ink::runtime::internal::managed_restorable_stack; template void stack_matches(const simple_restorable_stack& stack, T (&expected)[SIZE]) { // Iterate stack and check that it matches the expected array - int count = 0; - const int* iter = nullptr; + int count = 0; + const int* iter = nullptr; while (stack.iter(iter)) { REQUIRE(count < SIZE); REQUIRE(*iter == expected[SIZE - count - 1]); @@ -20,47 +20,46 @@ void stack_matches(const simple_restorable_stack& stack, T (&expected)[SIZE]) } } -SCENARIO("simple_restorable_stack can be pushed and popped", "[stack]") { +SCENARIO("simple_restorable_stack can be pushed and popped", "[stack][unit][internals]") +{ - GIVEN("An empty stack") { + GIVEN("An empty stack") + { fixed_restorable_stack stack(~0); - WHEN("items are added") { + WHEN("items are added") + { stack.push(1); stack.push(2); stack.push(3); - THEN("the size has increased") { - REQUIRE(!stack.empty()); + THEN("the size has increased") + { + REQUIRE(! stack.empty()); REQUIRE(stack.size() == 3); } - THEN("the correct item is at the top") { - REQUIRE(stack.top() == 3); - } + THEN("the correct item is at the top") { REQUIRE(stack.top() == 3); } - THEN("we can iterate the stack") { - int expected[] = { 1, 2, 3 }; + THEN("we can iterate the stack") + { + int expected[] = {1, 2, 3}; stack_matches(stack, expected); } - WHEN("they are popped") { + WHEN("they are popped") + { int pop = stack.pop(); - THEN("the correct item is popped") { - REQUIRE(pop == 3); - } + THEN("the correct item is popped") { REQUIRE(pop == 3); } - THEN("there are fewer items") { - REQUIRE(stack.size() == 2); - } + THEN("there are fewer items") { REQUIRE(stack.size() == 2); } - THEN("there is a new item on top") { - REQUIRE(stack.top() == 2); - } + THEN("there is a new item on top") { REQUIRE(stack.top() == 2); } - THEN("we can still iterate") { - int expected[] = { 1, 2 }; + THEN("we can still iterate") + { + int expected[] = {1, 2}; stack_matches(stack, expected); } } @@ -68,82 +67,92 @@ SCENARIO("simple_restorable_stack can be pushed and popped", "[stack]") { } } -SCENARIO("simple_restorable_stack supports save/restore", "[stack]") { - GIVEN("a stack with a few items that has been saved") { +SCENARIO("simple_restorable_stack supports save/restore", "[stack][unit][internals]") +{ + GIVEN("a stack with a few items that has been saved") + { fixed_restorable_stack stack(~0); - stack.push(1); + stack.push(1); stack.push(2); stack.push(3); stack.save(); auto check_restore = [&stack]() { - WHEN("we restore the stack") { + WHEN("we restore the stack") + { stack.restore(); - THEN("we should be back to having 4 items") { - REQUIRE(stack.size() == 3); - } + THEN("we should be back to having 4 items") { REQUIRE(stack.size() == 3); } - THEN("they should match exactly the original items") { - int expected[] = { 1, 2, 3 }; + THEN("they should match exactly the original items") + { + int expected[] = {1, 2, 3}; stack_matches(stack, expected); } } }; - WHEN("new items are pushed") { + WHEN("new items are pushed") + { stack.push(4); - THEN("the pushed item is on top") { - REQUIRE(stack.top() == 4); - } - THEN("the size has increased") { - REQUIRE(stack.size() == 4); - } + THEN("the pushed item is on top") { REQUIRE(stack.top() == 4); } + THEN("the size has increased") { REQUIRE(stack.size() == 4); } - WHEN("the state is restored") { + WHEN("the state is restored") + { stack.restore(); - THEN("the original item is back on top") { + THEN("the original item is back on top") + { REQUIRE(stack.size() == 3); REQUIRE(stack.top() == 3); } } - WHEN("the state is finalized") { + WHEN("the state is finalized") + { stack.forget(); - THEN("the new item is still on top") { + THEN("the new item is still on top") + { REQUIRE(stack.size() == 4); REQUIRE(stack.top() == 4); } } } - WHEN("items are popped") { - stack.pop(); stack.pop(); + WHEN("items are popped") + { + stack.pop(); + stack.pop(); - THEN("the stack has shrunk") { + THEN("the stack has shrunk") + { REQUIRE(stack.size() == 1); REQUIRE(stack.top() == 1); } - WHEN("more items are pushed") { + WHEN("more items are pushed") + { stack.push(90); stack.push(91); - THEN("the stack has the correct size") { + THEN("the stack has the correct size") + { REQUIRE(stack.size() == 3); REQUIRE(stack.top() == 91); } - THEN("iteration only covers items that ought to be in the stack") { - int expected[] = { 1, 90, 91 }; + THEN("iteration only covers items that ought to be in the stack") + { + int expected[] = {1, 90, 91}; stack_matches(stack, expected); } - THEN("we can pop all items from the stack") { + THEN("we can pop all items from the stack") + { REQUIRE(stack.pop() == 91); REQUIRE(stack.pop() == 90); REQUIRE(stack.pop() == 1); @@ -157,7 +166,7 @@ SCENARIO("simple_restorable_stack supports save/restore", "[stack]") { // Push more items stack.push(100); stack.push(101); - int expected[] = { 100, 101 }; + int expected[] = {100, 101}; stack_matches(stack, expected); // Check that we can still restore @@ -168,17 +177,21 @@ SCENARIO("simple_restorable_stack supports save/restore", "[stack]") { // Check that the stack can restore check_restore(); - THEN("we can finalize the stack") { + THEN("we can finalize the stack") + { stack.forget(); - int expected[] = { 1, 90, 91 }; + int expected[] = {1, 90, 91}; stack_matches(stack, expected); } - WHEN("we pop them back off") { - stack.pop(); stack.pop(); + WHEN("we pop them back off") + { + stack.pop(); + stack.pop(); - THEN("the correct entry is on top") { + THEN("the correct entry is on top") + { REQUIRE(stack.top() == 1); REQUIRE(stack.size() == 1); } @@ -201,7 +214,7 @@ SCENARIO("simple_restorable_stack supports save/restore", "[stack]") { THEN("the stack should be considered 'empty'") { - REQUIRE(stack.size() == 0); + REQUIRE(stack.size() == 0); REQUIRE(stack.empty()); } } @@ -218,7 +231,6 @@ SCENARIO("simple_restorable_stack supports save/restore", "[stack]") { REQUIRE(stack.size() == 0); REQUIRE(stack.empty()); } - } } } diff --git a/inkcpp_test/Tags.cpp b/inkcpp_test/Tags.cpp index 17ffb06d..02e425d9 100644 --- a/inkcpp_test/Tags.cpp +++ b/inkcpp_test/Tags.cpp @@ -10,291 +10,317 @@ using namespace ink::runtime; -SCENARIO("tags", "[ahf]") +SCENARIO("tags", "[tags][runtime]") { - std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "AHF.bin")}; - runner thread = ink->new_runner(); - thread->move_to(ink::hash_string("test_knot")); - while (thread->can_continue()) { - auto line = thread->getline(); + GIVEN("a story moved to test_knot") + { + std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "AHF.bin")}; + runner thread = ink->new_runner(); + thread->move_to(ink::hash_string("test_knot")); + + WHEN("all lines are consumed") + { + while (thread->can_continue()) { + thread->getline(); + } + + THEN("the thread cannot continue") { REQUIRE(thread->can_continue() == false); } + } } - REQUIRE(thread->can_continue() == false); } -SCENARIO("run story with tags", "[tags][story]") +SCENARIO("run story with tags", "[tags][runtime]") { GIVEN("a story with tags") { - std::unique_ptr _ink{story::from_file(INK_TEST_RESOURCE_DIR "TagsStory.bin")}; - runner _thread = _ink->new_runner(); - WHEN("starting the thread") + std::unique_ptr ink{story::from_file(INK_TEST_RESOURCE_DIR "TagsStory.bin")}; + runner thread = ink->new_runner(); + + THEN("initially there are no tags and the runner has not moved to any knot") { - CHECK_FALSE(_thread->has_tags()); - CHECK(_thread->get_current_knot() == 0); + CHECK_FALSE(thread->has_tags()); + CHECK(thread->get_current_knot() == 0); } - WHEN("on the first line") + + WHEN("the first line is read") { - CHECK(_thread->getline() == "First line has global tags only\n"); - THEN("it has the global tags") + std::string line = thread->getline(); + + THEN("the output is correct and only global tags are present") { - CHECK(_thread->get_current_knot() == ink::hash_string("global_tags_only")); - CHECK(_thread->has_global_tags()); - CHECK_FALSE(_thread->has_knot_tags()); - CHECK(_thread->has_tags()); - REQUIRE(_thread->num_global_tags() == 1); - CHECK(std::string(_thread->get_global_tag(0)) == "global_tag"); - REQUIRE(_thread->num_tags() == 1); - CHECK(std::string(_thread->get_tag(0)) == "global_tag"); + REQUIRE(line == "First line has global tags only\n"); + CHECK(thread->get_current_knot() == ink::hash_string("global_tags_only")); + CHECK(thread->has_global_tags()); + CHECK_FALSE(thread->has_knot_tags()); + CHECK(thread->has_tags()); + REQUIRE(thread->num_global_tags() == 1); + CHECK(std::string(thread->get_global_tag(0)) == "global_tag"); + REQUIRE(thread->num_tags() == 1); + CHECK(std::string(thread->get_tag(0)) == "global_tag"); } } - WHEN("on the second line") + + WHEN("the second line is read") { - _thread->getline(); - CHECK(_thread->getline() == "Second line has one tag\n"); - THEN("it has one tag") + // skip line 1 + thread->getline(); + std::string line = thread->getline(); + + THEN("the output is correct and one inline tag is present") { - CHECK(_thread->get_current_knot() == ink::hash_string("global_tags_only")); - CHECK(_thread->has_tags()); - CHECK(_thread->num_global_tags() == 1); - CHECK(_thread->num_knot_tags() == 0); - REQUIRE(_thread->num_tags() == 1); - CHECK(std::string(_thread->get_tag(0)) == "tagged"); + REQUIRE(line == "Second line has one tag\n"); + CHECK(thread->get_current_knot() == ink::hash_string("global_tags_only")); + CHECK(thread->has_tags()); + CHECK(thread->num_global_tags() == 1); + CHECK(thread->num_knot_tags() == 0); + REQUIRE(thread->num_tags() == 1); + CHECK(std::string(thread->get_tag(0)) == "tagged"); } } - WHEN("on the third line") + + WHEN("the third line is read") { - _thread->getline(); - _thread->getline(); - CHECK(_thread->getline() == "Third line has two tags\n"); - THEN("it has two tags") + // skip lines 1-2 + for (int i = 0; i < 2; ++i) { + thread->getline(); + } + std::string line = thread->getline(); + + THEN("the output is correct and two inline tags are present") { - CHECK(_thread->get_current_knot() == ink::hash_string("global_tags_only")); - CHECK(_thread->has_tags()); - CHECK(_thread->num_global_tags() == 1); - CHECK(_thread->num_knot_tags() == 0); - REQUIRE(_thread->num_tags() == 2); - CHECK(std::string(_thread->get_tag(0)) == "tag next line"); - CHECK(std::string(_thread->get_tag(1)) == "more tags"); + REQUIRE(line == "Third line has two tags\n"); + CHECK(thread->get_current_knot() == ink::hash_string("global_tags_only")); + CHECK(thread->has_tags()); + CHECK(thread->num_global_tags() == 1); + CHECK(thread->num_knot_tags() == 0); + REQUIRE(thread->num_tags() == 2); + CHECK(std::string(thread->get_tag(0)) == "tag next line"); + CHECK(std::string(thread->get_tag(1)) == "more tags"); } } - WHEN("on the fourth line") + + WHEN("the fourth line is read") { - _thread->getline(); - _thread->getline(); - _thread->getline(); - CHECK(_thread->getline() == "Fourth line has three tags\n"); + // skip lines 1-3 + for (int i = 0; i < 3; ++i) { + thread->getline(); + } + std::string line = thread->getline(); - THEN("it has three tags") + THEN("the output is correct and three inline tags are present") { - CHECK(_thread->get_current_knot() == ink::hash_string("global_tags_only")); - CHECK(_thread->has_tags()); - CHECK(_thread->num_global_tags() == 1); - CHECK(_thread->num_knot_tags() == 0); - REQUIRE(_thread->num_tags() == 3); - CHECK(std::string(_thread->get_tag(0)) == "above"); - CHECK(std::string(_thread->get_tag(1)) == "side"); - CHECK(std::string(_thread->get_tag(2)) == "across"); + REQUIRE(line == "Fourth line has three tags\n"); + CHECK(thread->get_current_knot() == ink::hash_string("global_tags_only")); + CHECK(thread->has_tags()); + CHECK(thread->num_global_tags() == 1); + CHECK(thread->num_knot_tags() == 0); + REQUIRE(thread->num_tags() == 3); + CHECK(std::string(thread->get_tag(0)) == "above"); + CHECK(std::string(thread->get_tag(1)) == "side"); + CHECK(std::string(thread->get_tag(2)) == "across"); } } - WHEN("entering a knot") + + WHEN("the knot entry line is read") { - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - CHECK(_thread->getline() == "Hello\n"); - THEN("it has four tags") + // skip lines 1-4 + for (int i = 0; i < 4; ++i) { + thread->getline(); + } + std::string line = thread->getline(); + + THEN("the output is correct and knot tags are combined with global and inline tags") { - CHECK(_thread->get_current_knot() == ink::hash_string("start")); - CHECK(_thread->has_tags()); - CHECK(_thread->num_global_tags() == 1); - REQUIRE(_thread->num_tags() == 4); - CHECK(std::string(_thread->get_tag(0)) == "knot_tag_start"); - CHECK(std::string(_thread->get_tag(1)) == "second_knot_tag_start"); - CHECK(std::string(_thread->get_tag(2)) == "third_knot_tag"); - CHECK(std::string(_thread->get_tag(3)) == "output_tag_h"); - REQUIRE(_thread->num_knot_tags() == 3); - CHECK(std::string(_thread->get_knot_tag(0)) == "knot_tag_start"); - CHECK(std::string(_thread->get_knot_tag(1)) == "second_knot_tag_start"); - CHECK(std::string(_thread->get_knot_tag(2)) == "third_knot_tag"); + REQUIRE(line == "Hello\n"); + CHECK(thread->get_current_knot() == ink::hash_string("start")); + CHECK(thread->has_tags()); + CHECK(thread->num_global_tags() == 1); + REQUIRE(thread->num_tags() == 4); + CHECK(std::string(thread->get_tag(0)) == "knot_tag_start"); + CHECK(std::string(thread->get_tag(1)) == "second_knot_tag_start"); + CHECK(std::string(thread->get_tag(2)) == "third_knot_tag"); + CHECK(std::string(thread->get_tag(3)) == "output_tag_h"); + REQUIRE(thread->num_knot_tags() == 3); + CHECK(std::string(thread->get_knot_tag(0)) == "knot_tag_start"); + CHECK(std::string(thread->get_knot_tag(1)) == "second_knot_tag_start"); + CHECK(std::string(thread->get_knot_tag(2)) == "third_knot_tag"); } } - WHEN("on the next line") + + WHEN("the line after the knot header is read") { - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - CHECK(_thread->getline() == "Second line has no tags\n"); - THEN("it has no tags") + // skip lines 1-5 + for (int i = 0; i < 5; ++i) { + thread->getline(); + } + std::string line = thread->getline(); + + THEN("the output is correct and no inline tags are present") { - CHECK(_thread->get_current_knot() == ink::hash_string("start")); - CHECK(_thread->num_global_tags() == 1); - CHECK(_thread->num_knot_tags() == 3); - CHECK_FALSE(_thread->has_tags()); - REQUIRE(_thread->num_tags() == 0); + REQUIRE(line == "Second line has no tags\n"); + CHECK(thread->get_current_knot() == ink::hash_string("start")); + CHECK(thread->num_global_tags() == 1); + CHECK(thread->num_knot_tags() == 3); + CHECK_FALSE(thread->has_tags()); + REQUIRE(thread->num_tags() == 0); } } - WHEN("at the first choice list") + + WHEN("all six lines are consumed and the first choice list appears") { - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - CHECK_FALSE(_thread->can_continue()); - - REQUIRE(std::distance(_thread->begin(), _thread->end()) == 2); - auto choice_list = _thread->begin(); - - THEN("check tags on choices") + for (int i = 0; i < 6; ++i) { + thread->getline(); + } + auto choices = thread->begin(); + + THEN("the runner is at a choice point with two correctly tagged options") { - CHECK(_thread->get_current_knot() == ink::hash_string("start")); - CHECK(_thread->num_global_tags() == 1); - CHECK(_thread->num_knot_tags() == 3); - CHECK(std::string(choice_list[0].text()) == "a"); - CHECK_FALSE(choice_list[0].has_tags()); - REQUIRE(choice_list[0].num_tags() == 0); - - CHECK(std::string(choice_list[1].text()) == "b"); - CHECK(choice_list[1].has_tags()); - REQUIRE(choice_list[1].num_tags() == 2); - CHECK(std::string(choice_list[1].get_tag(0)) == "choice_tag_b"); - CHECK(std::string(choice_list[1].get_tag(1)) == "choice_tag_b_2"); + CHECK_FALSE(thread->can_continue()); + CHECK(thread->get_current_knot() == ink::hash_string("start")); + CHECK(thread->num_global_tags() == 1); + CHECK(thread->num_knot_tags() == 3); + REQUIRE(std::distance(thread->begin(), thread->end()) == 2); + + CHECK(std::string(choices[0].text()) == "a"); + CHECK_FALSE(choices[0].has_tags()); + REQUIRE(choices[0].num_tags() == 0); + + CHECK(std::string(choices[1].text()) == "b"); + CHECK(choices[1].has_tags()); + REQUIRE(choices[1].num_tags() == 2); + CHECK(std::string(choices[1].get_tag(0)) == "choice_tag_b"); + CHECK(std::string(choices[1].get_tag(1)) == "choice_tag_b_2"); } } - WHEN("selecting the second choice") + + WHEN("choice 'b' is selected at the first choice list") { - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->choose(1); - - CHECK(_thread->getline() == "Knot2\n"); - THEN("it has two tags") + for (int i = 0; i < 6; ++i) { + thread->getline(); + } + thread->choose(1); + std::string line = thread->getline(); + + THEN("the output is correct and the new knot's tags are reflected") { - CHECK(_thread->get_current_knot() == ink::hash_string("knot2.sub")); - INFO(_thread->get_knot_tag(0)); - CHECK(_thread->num_global_tags() == 1); - CHECK(_thread->has_tags()); - REQUIRE(_thread->num_tags() == 2); - CHECK(std::string(_thread->get_tag(0)) == "knot_tag_2"); - CHECK(std::string(_thread->get_tag(1)) == "output_tag_k"); - CHECK(_thread->has_knot_tags()); - REQUIRE(_thread->num_knot_tags() == 1); - CHECK(std::string(_thread->get_knot_tag(0)) == "knot_tag_2"); + REQUIRE(line == "Knot2\n"); + CHECK(thread->get_current_knot() == ink::hash_string("knot2.sub")); + INFO(thread->get_knot_tag(0)); + CHECK(thread->num_global_tags() == 1); + CHECK(thread->has_tags()); + REQUIRE(thread->num_tags() == 2); + CHECK(std::string(thread->get_tag(0)) == "knot_tag_2"); + CHECK(std::string(thread->get_tag(1)) == "output_tag_k"); + CHECK(thread->has_knot_tags()); + REQUIRE(thread->num_knot_tags() == 1); + CHECK(std::string(thread->get_knot_tag(0)) == "knot_tag_2"); } } - WHEN("jumping to a knot") + + WHEN("the runner jumps directly to knot2") { - _thread->move_to(ink::hash_string("knot2")); - REQUIRE(_thread->getline() == "Knot2\n"); - THEN("global tags are missing") + thread->move_to(ink::hash_string("knot2")); + std::string line = thread->getline(); + + THEN("the output is correct and global tags are absent since the global section was skipped") { - CHECK(_thread->get_current_knot() == ink::hash_string("knot2.sub")); - CHECK(_thread->num_global_tags() == 0); - CHECK(_thread->has_tags()); - REQUIRE(_thread->num_tags() == 2); - CHECK(std::string(_thread->get_tag(0)) == "knot_tag_2"); - CHECK(std::string(_thread->get_tag(1)) == "output_tag_k"); - CHECK(_thread->has_knot_tags()); - REQUIRE(_thread->num_knot_tags() == 1); - CHECK(std::string(_thread->get_knot_tag(0)) == "knot_tag_2"); + REQUIRE(line == "Knot2\n"); + CHECK(thread->get_current_knot() == ink::hash_string("knot2.sub")); + CHECK(thread->num_global_tags() == 0); + CHECK(thread->has_tags()); + REQUIRE(thread->num_tags() == 2); + CHECK(std::string(thread->get_tag(0)) == "knot_tag_2"); + CHECK(std::string(thread->get_tag(1)) == "output_tag_k"); + CHECK(thread->has_knot_tags()); + REQUIRE(thread->num_knot_tags() == 1); + CHECK(std::string(thread->get_knot_tag(0)) == "knot_tag_2"); } } - WHEN("at the second choice list") + + WHEN("choice 'b' is selected and the second choice list appears") { - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->choose(1); - _thread->getline(); - CHECK(! _thread->can_continue()); - - REQUIRE(std::distance(_thread->begin(), _thread->end()) == 3); - auto choice_list = _thread->begin(); - - THEN("check tags on choices") + for (int i = 0; i < 6; ++i) { + thread->getline(); + } + thread->choose(1); + thread->getline(); // consume "Knot2" + auto choices = thread->begin(); + + THEN("three choices are present with correct tags") { - CHECK(_thread->get_current_knot() == ink::hash_string("knot2.sub")); - CHECK(_thread->num_global_tags() == 1); - CHECK(_thread->num_knot_tags() == 1); - CHECK(_thread->num_tags() == 2); - CHECK(std::string(choice_list[0].text()) == "e"); - CHECK_FALSE(choice_list[0].has_tags()); - REQUIRE(choice_list[0].num_tags() == 0); - - CHECK(std::string(choice_list[1].text()) == "f with detail"); - CHECK(choice_list[1].has_tags()); - REQUIRE(choice_list[1].num_tags() == 4); - CHECK(std::string(choice_list[1].get_tag(0)) == "shared_tag"); - CHECK(std::string(choice_list[1].get_tag(1)) == "shared_tag_2"); - CHECK(std::string(choice_list[1].get_tag(2)) == "choice_tag"); - CHECK(std::string(choice_list[1].get_tag(3)) == "choice_tag_2"); - - CHECK(std::string(choice_list[2].text()) == "g"); - CHECK(choice_list[2].has_tags()); - REQUIRE(choice_list[2].num_tags() == 1); - CHECK(std::string(choice_list[2].get_tag(0)) == "choice_tag_g"); + CHECK_FALSE(thread->can_continue()); + CHECK(thread->get_current_knot() == ink::hash_string("knot2.sub")); + CHECK(thread->num_global_tags() == 1); + CHECK(thread->num_knot_tags() == 1); + CHECK(thread->num_tags() == 2); + REQUIRE(std::distance(thread->begin(), thread->end()) == 3); + + CHECK(std::string(choices[0].text()) == "e"); + CHECK_FALSE(choices[0].has_tags()); + REQUIRE(choices[0].num_tags() == 0); + + CHECK(std::string(choices[1].text()) == "f with detail"); + CHECK(choices[1].has_tags()); + REQUIRE(choices[1].num_tags() == 4); + CHECK(std::string(choices[1].get_tag(0)) == "shared_tag"); + CHECK(std::string(choices[1].get_tag(1)) == "shared_tag_2"); + CHECK(std::string(choices[1].get_tag(2)) == "choice_tag"); + CHECK(std::string(choices[1].get_tag(3)) == "choice_tag_2"); + + CHECK(std::string(choices[2].text()) == "g"); + CHECK(choices[2].has_tags()); + REQUIRE(choices[2].num_tags() == 1); + CHECK(std::string(choices[2].get_tag(0)) == "choice_tag_g"); } } - WHEN("selecting the choice with shared tags") + + WHEN("choice 'b' then choice 'f with detail' is selected") { - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->choose(1); - _thread->getline(); - _thread->choose(1); - - REQUIRE(_thread->getline() == "f and content\n"); - THEN("it has four tags") + for (int i = 0; i < 6; ++i) { + thread->getline(); + } + thread->choose(1); + thread->getline(); // "Knot2" + thread->choose(1); + std::string line = thread->getline(); + + THEN("the output is correct and four tags combining shared and content tags are present") { - CHECK(_thread->get_current_knot() == ink::hash_string("knot2.sub")); - CHECK(_thread->num_global_tags() == 1); - CHECK(_thread->num_knot_tags() == 1); - CHECK(_thread->has_tags()); - REQUIRE(_thread->num_tags() == 4); - CHECK(std::string(_thread->get_tag(0)) == "shared_tag"); - CHECK(std::string(_thread->get_tag(1)) == "shared_tag_2"); - CHECK(std::string(_thread->get_tag(2)) == "content_tag"); - CHECK(std::string(_thread->get_tag(3)) == "content_tag_2"); + REQUIRE(line == "f and content\n"); + CHECK(thread->get_current_knot() == ink::hash_string("knot2.sub")); + CHECK(thread->num_global_tags() == 1); + CHECK(thread->num_knot_tags() == 1); + CHECK(thread->has_tags()); + REQUIRE(thread->num_tags() == 4); + CHECK(std::string(thread->get_tag(0)) == "shared_tag"); + CHECK(std::string(thread->get_tag(1)) == "shared_tag_2"); + CHECK(std::string(thread->get_tag(2)) == "content_tag"); + CHECK(std::string(thread->get_tag(3)) == "content_tag_2"); } } - WHEN("on the last line") + + WHEN("the story is played through to the final line") { - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->getline(); - _thread->choose(1); - _thread->getline(); - _thread->choose(1); - _thread->getline(); - CHECK(_thread->getline() == "out\n"); - THEN("it has one tag") + for (int i = 0; i < 6; ++i) { + thread->getline(); + } + thread->choose(1); + thread->getline(); // "Knot2" + thread->choose(1); + thread->getline(); // "f and content" + std::string line = thread->getline(); + + THEN("the output is correct and one closing tag is present alongside global and knot tags") { - CHECK(_thread->get_current_knot() == ink::hash_string("knot2.sub")); - CHECK(_thread->num_global_tags() == 1); - CHECK(std::string(_thread->get_global_tag(0)) == "global_tag"); - REQUIRE(_thread->num_knot_tags() == 1); - CHECK(std::string(_thread->get_knot_tag(0)) == "knot_tag_2"); - CHECK(_thread->has_tags()); - REQUIRE(_thread->num_tags() == 1); - CHECK(std::string(_thread->get_tag(0)) == "close_tag"); + REQUIRE(line == "out\n"); + CHECK(thread->get_current_knot() == ink::hash_string("knot2.sub")); + CHECK(thread->num_global_tags() == 1); + CHECK(std::string(thread->get_global_tag(0)) == "global_tag"); + REQUIRE(thread->num_knot_tags() == 1); + CHECK(std::string(thread->get_knot_tag(0)) == "knot_tag_2"); + CHECK(thread->has_tags()); + REQUIRE(thread->num_tags() == 1); + CHECK(std::string(thread->get_tag(0)) == "close_tag"); } } } diff --git a/inkcpp_test/ThirdTierChoiceAfterBrackets.cpp b/inkcpp_test/ThirdTierChoiceAfterBrackets.cpp index 207baae1..a643c859 100644 --- a/inkcpp_test/ThirdTierChoiceAfterBrackets.cpp +++ b/inkcpp_test/ThirdTierChoiceAfterBrackets.cpp @@ -9,7 +9,7 @@ using namespace ink::runtime; SCENARIO( "a story with a bracketed choice as a second choice, and then a third choice, chooses properly", - "[choices]" + "[choices][runtime]" ) { GIVEN("a story with brackets and nested choices") @@ -18,21 +18,34 @@ SCENARIO( "ThirdTierChoiceAfterBracketsStory.bin")}; runner thread = ink->new_runner(); - WHEN("start thread") + WHEN("the story starts") { - THEN("thread doesn't error") + thread->getall(); + + THEN("the first tier choices are presented") { REQUIRE(thread->has_choices()); } + + AND_WHEN("the first choice is made") { - thread->getall(); - REQUIRE(thread->has_choices()); - thread->choose(0); - thread->getall(); - REQUIRE(thread->has_choices()); - thread->choose(0); - thread->getall(); - REQUIRE(thread->has_choices()); thread->choose(0); thread->getall(); - REQUIRE(! thread->has_choices()); + + THEN("the second tier choices are presented") { REQUIRE(thread->has_choices()); } + + AND_WHEN("the second choice is made") + { + thread->choose(0); + thread->getall(); + + THEN("the third tier choices are presented") { REQUIRE(thread->has_choices()); } + + AND_WHEN("the third choice is made") + { + thread->choose(0); + thread->getall(); + + THEN("the story ends with no further choices") { REQUIRE_FALSE(thread->has_choices()); } + } + } } } } diff --git a/inkcpp_test/UTF8.cpp b/inkcpp_test/UTF8.cpp index 4b2c7beb..732a9c2f 100644 --- a/inkcpp_test/UTF8.cpp +++ b/inkcpp_test/UTF8.cpp @@ -11,7 +11,7 @@ using namespace ink::runtime; -SCENARIO("a story supports UTF-8", "[utf-8]") +SCENARIO("a story supports UTF-8", "[utf-8][compiler][runtime]") { GIVEN("a story with UTF8 characters") { diff --git a/inkcpp_test/Value.cpp b/inkcpp_test/Value.cpp index e2615cfd..56039bc1 100644 --- a/inkcpp_test/Value.cpp +++ b/inkcpp_test/Value.cpp @@ -37,7 +37,7 @@ void cp_str(char* dst, const char* src) *dst = 0; } -SCENARIO("compare concatenated values") +SCENARIO("compare concatenated values", "[values][unit][internals]") { string_table str_table; list_table lst_table{}; From 03c5cd353bea33667a0241ec7807f7f144a334f8 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 5 Jun 2026 15:54:03 +0200 Subject: [PATCH 19/29] test: restructure test cases to be more fitting with BDD --- inkcpp/array.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/inkcpp/array.h b/inkcpp/array.h index 87f8137d..5d02dac2 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -458,6 +458,8 @@ inline void basic_restorable_array::forget() // Clear _temp[i] = _null; } + + _saved = false; } template @@ -582,8 +584,8 @@ inline size_t basic_restorable_array::snap(unsigned char* data, const snapper } template -inline const unsigned char* - basic_restorable_array::impl_snap_load_meta(const unsigned char* data) +inline const unsigned char* basic_restorable_array::impl_snap_load_meta(const unsigned char* data +) { auto ptr = data; ptr = snap_read(ptr, _saved); @@ -611,9 +613,8 @@ inline const unsigned char* } template -inline const unsigned char* fixed_restorable_array::snap_load( - const unsigned char* data, const snapshot_interface::loader& -) +inline const unsigned char* fixed_restorable_array< + T, SIZE>::snap_load(const unsigned char* data, const snapshot_interface::loader&) { auto ptr = data; ptr = base::impl_snap_load_meta(ptr); @@ -622,9 +623,8 @@ inline const unsigned char* fixed_restorable_array::snap_load( } template -inline const unsigned char* allocated_restorable_array::snap_load( - const unsigned char* data, const snapshot_interface::loader& -) +inline const unsigned char* allocated_restorable_array< + T>::snap_load(const unsigned char* data, const snapshot_interface::loader&) { auto ptr = data; ptr = base::impl_snap_load_meta(ptr); From 65cb33d4e90da484ca48ed78f7f89afffe271efc Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 5 Jun 2026 17:16:27 +0200 Subject: [PATCH 20/29] WIP --- inkcpp/globals_impl.cpp | 8 ++++++-- inkcpp/globals_impl.h | 10 +++++++--- inkcpp/runner_impl.cpp | 12 +++++++----- inkcpp/runner_impl.h | 6 ++++-- inkcpp_test/Migration.cpp | 12 ++++++------ inkcpp_test/ink/MigrationBase.ink | 1 + inkcpp_test/ink/MigrationChangeNodes.ink | 1 + inkcpp_test/ink/MigrationKnotTags.ink | 1 + inkcpp_test/ink/MigrationTemp.ink | 1 + 9 files changed, 34 insertions(+), 18 deletions(-) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 18f40121..ab79019d 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -30,9 +30,13 @@ globals_impl::globals_impl(const story_impl* story) } } -void globals_impl::visit(uint32_t container_id) +void globals_impl::visit(uint32_t container_id, bool preserve_turns) { - _visit_counts.set(container_id, {_visit_counts[container_id].visits + 1, 0}); + const int32_t existing_turns = _visit_counts[container_id].turns; + _visit_counts.set( + container_id, {_visit_counts[container_id].visits + (preserve_turns ? 0 : 1), + preserve_turns ? existing_turns : 0} + ); } uint32_t globals_impl::visits(uint32_t container_id) const diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index caf9dd83..854a3ec8 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -43,7 +43,9 @@ class globals_impl final * @param[in] list_metadata old list metadata to migrate list * the globals stored inside. */ - bool migrate_new_globals(const loader& loader, globals_impl& new_globals, const char* list_metadata); + bool migrate_new_globals( + const loader& loader, globals_impl& new_globals, const char* list_metadata + ); // Initializes a new global store from the given story globals_impl(const story_impl*); @@ -62,8 +64,10 @@ class globals_impl final void internal_observe(hash_t name, internal::callback_base* callback) override; public: - // Records a visit to a container - void visit(uint32_t container_id); + // Records a visit to a container. + // If preserve_turns is true the existing turns-since counter is kept intact + // (used during snapshot migration to avoid clobbering the restored value). + void visit(uint32_t container_id, bool preserve_turns = false); // Checks the number of visits to a container uint32_t visits(uint32_t container_id) const; diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 50b09072..74e460ab 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -317,7 +317,7 @@ void runner_impl::fetch_tags(ip_t begin) } } -void runner_impl::jump(ip_t dest, bool record_visits, bool track_knot_visit) +void runner_impl::jump(ip_t dest, bool record_visits, bool track_knot_visit, bool preserve_turns) { // Optimization: if we are _is_falling, then we can // _should be_ able to safely assume that there is nothing to do here. A falling @@ -353,7 +353,7 @@ void runner_impl::jump(ip_t dest, bool record_visits, bool track_knot_visit) if (dest_offset == dest_container._start_offset) { // Record direct jump to non-knot if requested. (Knots handled below.) if (record_visits && ! dest_container.knot()) { - _globals->visit(dest_id); + _globals->visit(dest_id, preserve_turns); } // Consume instruction so we don't process it again during normal flow. (We need to do this here @@ -386,7 +386,7 @@ void runner_impl::jump(ip_t dest, bool record_visits, bool track_knot_visit) // Ink has a rule about incrementing visit counts when you jump to the top of a knot, which // seems to need to override inkcpp's knot_visit flag. if (track_knot_visit || container._start_offset == dest_offset) { - _globals->visit(id); + _globals->visit(id, preserve_turns); } // If tracking, update with the first knot we encounter, which is the one closest to the top @@ -849,10 +849,12 @@ bool runner_impl::migrate_to(const loader& loader, hash_t path) } } } - // rebuild container stack to display new offsets + // rebuild container stack using new story offsets. + // preserve_turns=true keeps the turns-since counters restored from the snapshot intact; + // without this the visit() call inside jump() would reset them to 0. _container.clear(); _ptr = nullptr; - jump(destination, false, true); + jump(destination, false, true, true); if (loader.old_ref_table && ! _globals->lists().migrate_variables( diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index b01a4aad..04660116 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -215,8 +215,10 @@ class runner_impl // Fetch string only tags at Tag/Global level void fetch_tags(ip_t begin); - // Special code for jumping from the current IP to another - void jump(ip_t, bool record_visits, bool track_knot_visit); + // Special code for jumping from the current IP to another. + // preserve_turns: if true, existing turns-since counters on visited knots are not reset + // (used during snapshot migration to keep the restored turn values intact). + void jump(ip_t, bool record_visits, bool track_knot_visit, bool preserve_turns = false); uint32_t _current_knot_id = ~0U; // id to detect knot changes from the outside uint32_t _current_knot_id_backup = ~0U; uint32_t _entered_knot = false; // if we are in the first action after a jump to an snitch/knot diff --git a/inkcpp_test/Migration.cpp b/inkcpp_test/Migration.cpp index 88ac0da1..47ba9ecf 100644 --- a/inkcpp_test/Migration.cpp +++ b/inkcpp_test/Migration.cpp @@ -51,7 +51,7 @@ SCENARIO("Simple isolated migration tests.", "[migration][integration]") THEN("the story outputs the expected follow-up content") { - REQUIRE(base_thread->getall() == "A\ncatch\n5 3\n1 -1 1\nOh.\n"); + REQUIRE(base_thread->getall() == "A\ncatch\n5 3\n1 -1 1\n1 0 1\nOh.\n"); } } } @@ -140,7 +140,7 @@ SCENARIO("Simple isolated migration tests.", "[migration][integration]") THEN("the story continues with the new knot's output") { - REQUIRE(new_thread->getall() == "A\ncatch\n-1 1 1\nOh.\n"); + REQUIRE(new_thread->getall() == "A\ncatch\n-1 1 1\n0 1 1\nOh.\n"); } } } @@ -158,7 +158,7 @@ SCENARIO("Simple isolated migration tests.", "[migration][integration]") THEN("visit counts are migrated and the story continues with node-aware output") { - REQUIRE(new_thread->getall() == "A\ncatch\n1 1 1\nOh.\n"); + REQUIRE(new_thread->getall() == "A\ncatch\n1 -1 1\n1 0 1\nOh.\n"); } } } @@ -189,7 +189,7 @@ SCENARIO("Simple isolated migration tests.", "[migration][integration]") THEN("the story continues with updated temporary variable output at the same knot") { REQUIRE(new_thread->get_current_knot() == 0x25e83b84); - REQUIRE(after == "A\ncatch\n2 - 3\n1 -1 1\nOh.\n"); + REQUIRE(after == "A\ncatch\n2 - 3\n1 -1 1\n1 0 1\nOh.\n"); } } } @@ -211,7 +211,7 @@ SCENARIO("Simple isolated migration tests.", "[migration][integration]") THEN("the old temporary variable is preserved and the new one uses its default") { REQUIRE(new_thread->get_current_knot() == 0x25e83b84); - REQUIRE(after == "A\ncatch\n5 - 6\n1 -1 1\nOh.\n"); + REQUIRE(after == "A\ncatch\n5 - 6\n1 -1 1\n1 0 1\nOh.\n"); } } } @@ -266,7 +266,7 @@ SCENARIO("Simple isolated migration tests.", "[migration][integration]") THEN("the story continues normally from the migration point") { - REQUIRE(new_thread->getall() == "A\ncatch\n5 3\n1 -1 1\nOh.\n"); + REQUIRE(new_thread->getall() == "A\ncatch\n5 3\n1 -1 1\n1 0 1\nOh.\n"); } } } diff --git a/inkcpp_test/ink/MigrationBase.ink b/inkcpp_test/ink/MigrationBase.ink index 21f6ab51..c372f1df 100644 --- a/inkcpp_test/ink/MigrationBase.ink +++ b/inkcpp_test/ink/MigrationBase.ink @@ -22,5 +22,6 @@ This is a simple story. - catch {tKeep} {tOld} {TURNS_SINCE(-> Node1)} {TURNS_SINCE(-> OldNode)} {TURNS_SINCE(-> Main)} +{Node1} {OldNode} {Main} Oh. ->DONE diff --git a/inkcpp_test/ink/MigrationChangeNodes.ink b/inkcpp_test/ink/MigrationChangeNodes.ink index 62c7ca4e..50603ada 100644 --- a/inkcpp_test/ink/MigrationChangeNodes.ink +++ b/inkcpp_test/ink/MigrationChangeNodes.ink @@ -14,5 +14,6 @@ This is a simple story. * B - catch {TURNS_SINCE(-> Node1)} {TURNS_SINCE(-> NewNode)} {TURNS_SINCE(-> Main)} +{Node1} {NewNode} {Main} Oh. ->DONE diff --git a/inkcpp_test/ink/MigrationKnotTags.ink b/inkcpp_test/ink/MigrationKnotTags.ink index 9c4037b0..acf68777 100644 --- a/inkcpp_test/ink/MigrationKnotTags.ink +++ b/inkcpp_test/ink/MigrationKnotTags.ink @@ -22,5 +22,6 @@ This is a simple story. - catch {tKeep} {tOld} {TURNS_SINCE(-> Node1)} {TURNS_SINCE(-> OldNode)} {TURNS_SINCE(-> Main)} +{Node1} {OldNode} {Main} Oh. ->DONE diff --git a/inkcpp_test/ink/MigrationTemp.ink b/inkcpp_test/ink/MigrationTemp.ink index 422829f9..23afde11 100644 --- a/inkcpp_test/ink/MigrationTemp.ink +++ b/inkcpp_test/ink/MigrationTemp.ink @@ -22,5 +22,6 @@ This is a simple story. - catch {tKeep} - {tNew} {TURNS_SINCE(-> Node1)} {TURNS_SINCE(-> OldNode)} {TURNS_SINCE(-> Main)} +{Node1} {OldNode} {Main} Oh. ->DONE From 0149a072f5cb112e4f35e9f67bc58cc645778603 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 5 Jun 2026 22:13:54 +0200 Subject: [PATCH 21/29] ci: bump github actions to support NodeJS 24 --- inkcpp/globals_impl.cpp | 3 +++ inkcpp_python/pybind11 | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index ab79019d..40b7eedf 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -278,6 +278,9 @@ const unsigned char* globals_impl::snap_load(const unsigned char* ptr, const loa _visit_counts.resize(_owner->num_containers()); } _visit_counts.save(); + for (size_t i = 0; i < old_capacity; ++i) { + _visit_counts.set(i, visit_count()); + } } inkAssert( diff --git a/inkcpp_python/pybind11 b/inkcpp_python/pybind11 index 1b499083..0c69e1eb 160000 --- a/inkcpp_python/pybind11 +++ b/inkcpp_python/pybind11 @@ -1 +1 @@ -Subproject commit 1b4990838904501de7110d27e96c0a4152029156 +Subproject commit 0c69e1eb2177fa8f8580632c7b1f97fdb606ce8f From 31d0b757daa9c65a42c6ca16eb8614f011c6bf89 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 5 Jun 2026 22:53:31 +0200 Subject: [PATCH 22/29] WIP --- inkcpp_test/Migration.cpp | 47 +++++++++++++++++ inkcpp_test/ink/UE_example.ink | 2 +- unreal/CMakeLists.txt | 95 ++++++++++++++++++++++------------ 3 files changed, 110 insertions(+), 34 deletions(-) diff --git a/inkcpp_test/Migration.cpp b/inkcpp_test/Migration.cpp index 47ba9ecf..9b60899d 100644 --- a/inkcpp_test/Migration.cpp +++ b/inkcpp_test/Migration.cpp @@ -391,3 +391,50 @@ SCENARIO("Migratability of finished threads", "[migration][integration]") } } } + +SCENARIO("Migratibility of the UE_example story", "[migration][intigration]") +{ + GIVEN("The UE examplestory v1 and v2") + { + std::unique_ptr story_v1{story::from_file(INK_TEST_RESOURCE_DIR "UE_example.bin")}; + std::unique_ptr story_v2{story::from_file(INK_TEST_RESOURCE_DIR "UE_example_v2.bin")}; + runner thread = story_v1->new_runner(); + std::string last_target{}; + auto callback = [&last_target](const char* target) { + last_target = target; + }; + thread->bind("transition", callback, false); + WHEN("go to mansion in v1") + { + REQUIRE( + thread->getall() == "You step outside your car. Its a wired feeling beehing here again.\n" + ); + REQUIRE(last_target == ""); + REQUIRE(thread->num_choices() == 2); + thread->choose(1); + THEN("expect normal output") + { + REQUIRE(thread->getall() == "You startk walking to Mansion.Entrance.\nJust in time you are able to see the door, someone with with a yellow summer dress enters it.\nYou're climbing the 56 steps up to the door; high tides are an annoying thing.\n"); + REQUIRE(last_target == "Mansion.Entrance"); + } + } + WHEN("go to mansion in v1, but get after choice text in v2") + { + REQUIRE( + thread->getall() == "You step outside your car. Its a wired feeling beehing here again.\n" + ); + REQUIRE(last_target == ""); + REQUIRE(thread->num_choices() == 2); + thread->choose(1); + std::unique_ptr snap{thread->create_snapshot()}; + REQUIRE(snap->can_be_migrated()); + runner thread2 = story_v2->new_runner_from_snapshot(*snap); + thread2->bind("transition", callback, false); + THEN("the new text at the same location should be displayed") + { + REQUIRE(thread2->getall() == ""); + REQUIRE(last_target == "Mansion.Entrance"); + } + } + } +} diff --git a/inkcpp_test/ink/UE_example.ink b/inkcpp_test/ink/UE_example.ink index ab276a5a..71613f8d 100644 --- a/inkcpp_test/ink/UE_example.ink +++ b/inkcpp_test/ink/UE_example.ink @@ -102,4 +102,4 @@ You're climbing the 56 steps up to the door; high tides are an annoying thing. You just saw someone enter, how did they do not get shoked? It seems the developr run out of time, maybe in the next version we can continue? -> Entrance_cycle --> DONE \ No newline at end of file +-> DONE diff --git a/unreal/CMakeLists.txt b/unreal/CMakeLists.txt index 3000f45e..012cd036 100644 --- a/unreal/CMakeLists.txt +++ b/unreal/CMakeLists.txt @@ -1,64 +1,93 @@ -set(INKCPP_UNREAL_TARGET_VERSION "5.6" CACHE STRING "Unreal engine version the plugin should target (e.g: 5.6)") -set(INKCPP_UNREAL_RunUAT_PATH CACHE FILEPATH "Path to Unreal engine installation RunUAT file. Used to automatcally build the plugin.") +set(INKCPP_UNREAL_TARGET_VERSION + "5.7" + CACHE STRING "Unreal engine version the plugin should target (e.g: 5.6)") +set(INKCPP_UNREAL_RunUAT_PATH + CACHE FILEPATH + "Path to Unreal engine installation RunUAT file. Used to automatcally build the plugin.") option(INKCPP_UNREAL "Prepare sourcefiles for a UE Plugin (this will download " OFF) - option(INKCPP_DOC_BlueprintUE "Building doxygen documentation with BlueprintUE visualisation for unreal blueprints. (Requires node js)" ON) -set(INKCPP_UNREAL_TARGET_PLATFORM "Win64" CACHE STRING "Target platform for the UE Plugin one of Win64, Mac, Linux") +option(INKCPP_DOC_BlueprintUE "Building doxygen documentation with BlueprintUE visualisation " + "for unreal blueprints. (Requires node js)" ON) +set(INKCPP_UNREAL_TARGET_PLATFORM + "Win64" + CACHE STRING "Target platform for the UE Plugin one of Win64, Mac, Linux") set_property(CACHE INKCPP_UNREAL_TARGET_PLATFORM PROPERTY STRINGS "Win64" "Mac" "Linux") -if (INKCPP_UNREAL) +if(INKCPP_UNREAL) include(FetchContent) - configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/inkcpp/inkcpp.uplugin.in" - "${CMAKE_CURRENT_BINARY_DIR}/inkcpp/inkcpp.uplugin") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/inkcpp/inkcpp.uplugin.in" + "${CMAKE_CURRENT_BINARY_DIR}/inkcpp/inkcpp.uplugin") # download inklecate for unreal plugin FetchContent_MakeAvailable(inklecate_mac inklecate_windows inklecate_linux) set(FETCHCONTENT_QUIET OFF) set(CMAKE_TLS_VERIFY true) if(NOT inklecate_windows_SOURCE_DIR) - message(WARNING "failed to download inklecate for windows, " - "the unreal plugin will be unable use a .ink file as asset directly") + message(WARNING "failed to download inklecate for windows, " + "the unreal plugin will be unable use a .ink file as asset directly") else() - set(INKLECATE_CMD_WIN "Source/ThirdParty/inklecate/windows/inklecate.exe") - file(COPY "${CMAKE_BINARY_DIR}/inklecate/windows" DESTINATION "inkcpp/Source/ThirdParty/inklecate/") + set(INKLECATE_CMD_WIN "Source/ThirdParty/inklecate/windows/inklecate.exe") + file(COPY "${CMAKE_BINARY_DIR}/inklecate/windows" + DESTINATION "inkcpp/Source/ThirdParty/inklecate/") endif() if(NOT inklecate_mac_SOURCE_DIR) message(WARNING "failed to download inklecate for MacOS, " - "the unreal plugin will be unable use a .ink file as asset directly") + "the unreal plugin will be unable use a .ink file as asset directly") else() - set(INKLECATE_CMD_MAC "Source/ThirdParty/inklecate/mac/inklecate") - file(COPY "${CMAKE_BINARY_DIR}/inklecate/mac" DESTINATION "inkcpp/Source/ThirdParty/inklecate/") + set(INKLECATE_CMD_MAC "Source/ThirdParty/inklecate/mac/inklecate") + file(COPY "${CMAKE_BINARY_DIR}/inklecate/mac" DESTINATION "inkcpp/Source/ThirdParty/inklecate/") endif() if(NOT inklecate_linux_SOURCE_DIR) message(WARNING "failed to download inklecate for linux, " - "the unreal plugin will be unable use a .ink file as asset directly") + "the unreal plugin will be unable use a .ink file as asset directly") else() - set(INKLECATE_CMD_LINUX "Source/ThirdParty/inklecate/linux/inklecate") - file(COPY "${CMAKE_BINARY_DIR}/inklecate/linux" DESTINATION "inkcpp/Source/ThirdParty/inklecate/") + set(INKLECATE_CMD_LINUX "Source/ThirdParty/inklecate/linux/inklecate") + file(COPY "${CMAKE_BINARY_DIR}/inklecate/linux" + DESTINATION "inkcpp/Source/ThirdParty/inklecate/") endif() configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/inkcpp/Source/inkcpp_editor/Private/inklecate_cmd.cpp.in" - "${CMAKE_CURRENT_BINARY_DIR}/inkcpp/Source/inkcpp_editor/Private/inklecate_cmd.cpp" - ) - file(GLOB_RECURSE SOURCE_FILES LIST_DIRECTORIES TRUE RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/inkcpp/*") + "${CMAKE_CURRENT_BINARY_DIR}/inkcpp/Source/inkcpp_editor/Private/inklecate_cmd.cpp") + file( + GLOB_RECURSE SOURCE_FILES + LIST_DIRECTORIES TRUE + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/inkcpp/*") list(FILTER SOURCE_FILES EXCLUDE REGEX ".*\.in$") - foreach(SRC_FILE IN LISTS SOURCE_FILES) - if (NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FILE}") - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${SRC_FILE}" COPYONLY) + foreach(src_file IN LISTS SOURCE_FILES) + if(NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${src_file}") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${src_file}" + "${CMAKE_CURRENT_BINARY_DIR}/${src_file}" COPYONLY) endif() endforeach() - install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/inkcpp/" DESTINATION "inkcpp" COMPONENT unreal EXCLUDE_FROM_ALL) + install( + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/inkcpp/" + DESTINATION "inkcpp" + COMPONENT unreal + EXCLUDE_FROM_ALL) file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Plugins/inkcpp/") - if ((NOT DEFINED INKCPP_UNREAL_RunUAT_PATH) OR (NOT "${INKCPP_UNREAL_RunUAT_PATH}" STREQUAL "")) - if (NOT IS_READABLE "${INKCPP_UNREAL_RunUAT_PATH}") - message(WARNING "Unable to find RunUAT script at >${INKCPP_UNREAL_RunUAT_PATH}<, will not be able to build target `unreal` set the filepath with the variable INKCPP_UNREAL_RunUAT_PATH") + if((NOT DEFINED INKCPP_UNREAL_RunUAT_PATH) OR (NOT "${INKCPP_UNREAL_RunUAT_PATH}" STREQUAL "")) + if(NOT IS_READABLE "${INKCPP_UNREAL_RunUAT_PATH}") + message( + WARNING + "Unable to find RunUAT script at >${INKCPP_UNREAL_RunUAT_PATH}<, will not be able " + "to build target `unreal` set the filepath with the variable INKCPP_UNREAL_RunUAT_PATH") endif() else() - message(WARNING, "To directly build the plugin with `cmake --build . --target unreal` please set INKCPP_UNREAL_RunUAT_PATH to point to unreals RunUAT script.") + message(WARNING, "To directly build the plugin with `cmake --build . --target unreal` please " + "set INKCPP_UNREAL_RunUAT_PATH to point to unreals RunUAT script.") endif() - add_custom_target(unreal - "${INKCPP_UNREAL_RunUAT_PATH}" BuildPlugin "-plugin=${CMAKE_CURRENT_BINARY_DIR}/inkcpp/inkcpp.uplugin" "-package=${CMAKE_CURRENT_BINARY_DIR}/Plugins/inkcpp" "-TargetPlatforms=${INKCPP_UNREAL_TARGET_PLATFORM}" + add_custom_target( + unreal + "${INKCPP_UNREAL_RunUAT_PATH}" + BuildPlugin + "-plugin=${CMAKE_CURRENT_BINARY_DIR}/inkcpp/inkcpp.uplugin" + "-package=${CMAKE_CURRENT_BINARY_DIR}/Plugins/inkcpp" + "-TargetPlatforms=${INKCPP_UNREAL_TARGET_PLATFORM}" COMMENT "Compile UE Plugin.") - install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Plugins/inkcpp/" DESTINATION "inkcpp" COMPONENT unreal_plugin EXCLUDE_FROM_ALL PATTERN "Intermediate/*" EXCLUDE) + install( + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Plugins/inkcpp/" + DESTINATION "inkcpp" + COMPONENT unreal_plugin + EXCLUDE_FROM_ALL + PATTERN "Intermediate/*" EXCLUDE) # TODO: update documenation endif() - From 1f68685173206cac15222a001cbbdfee75e51958 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 8 Jun 2026 00:26:40 +0200 Subject: [PATCH 23/29] WIP --- unreal/CMakeLists.txt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/unreal/CMakeLists.txt b/unreal/CMakeLists.txt index 012cd036..6b304697 100644 --- a/unreal/CMakeLists.txt +++ b/unreal/CMakeLists.txt @@ -5,8 +5,8 @@ set(INKCPP_UNREAL_RunUAT_PATH CACHE FILEPATH "Path to Unreal engine installation RunUAT file. Used to automatcally build the plugin.") option(INKCPP_UNREAL "Prepare sourcefiles for a UE Plugin (this will download " OFF) -option(INKCPP_DOC_BlueprintUE "Building doxygen documentation with BlueprintUE visualisation " - "for unreal blueprints. (Requires node js)" ON) +option(INKCPP_DOC_BlueprintUE "Building doxygen documentation with BlueprintUE visualisation \ + for unreal blueprints. (Requires node js)" ON) set(INKCPP_UNREAL_TARGET_PLATFORM "Win64" CACHE STRING "Target platform for the UE Plugin one of Win64, Mac, Linux") @@ -66,13 +66,12 @@ if(INKCPP_UNREAL) if((NOT DEFINED INKCPP_UNREAL_RunUAT_PATH) OR (NOT "${INKCPP_UNREAL_RunUAT_PATH}" STREQUAL "")) if(NOT IS_READABLE "${INKCPP_UNREAL_RunUAT_PATH}") message( - WARNING - "Unable to find RunUAT script at >${INKCPP_UNREAL_RunUAT_PATH}<, will not be able " - "to build target `unreal` set the filepath with the variable INKCPP_UNREAL_RunUAT_PATH") + WARNING "Unable to find RunUAT script at >${INKCPP_UNREAL_RunUAT_PATH}<, will not be able \ + to build target `unreal` set the filepath with the variable INKCPP_UNREAL_RunUAT_PATH") endif() else() - message(WARNING, "To directly build the plugin with `cmake --build . --target unreal` please " - "set INKCPP_UNREAL_RunUAT_PATH to point to unreals RunUAT script.") + message(WARNING, "To directly build the plugin with `cmake --build . --target unreal` please \ + set INKCPP_UNREAL_RunUAT_PATH to point to unreals RunUAT script.") endif() add_custom_target( From b70cd9d4d08a4a67bc869d33d9916e98b185203a Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 8 Jun 2026 13:44:05 +0200 Subject: [PATCH 24/29] test: restructure test cases to be more fitting with BDD --- inkcpp/runner_impl.cpp | 30 +++++++++++++++++------------- inkcpp/story_impl.cpp | 18 ++++++++++++++++++ inkcpp/story_impl.h | 6 ++++++ inkcpp_python/pybind11 | 2 +- inkcpp_test/Migration.cpp | 27 ++++++++++++++++++++++++++- inkcpp_test/ink/UE_example_v2.ink | 2 +- 6 files changed, 69 insertions(+), 16 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 74e460ab..e14d13fe 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -655,13 +655,14 @@ bool runner_impl::can_be_migrated() const if (_entered_knot) { return false; } - container_t container_id - = _ptr != nullptr && _ptr >= _story->instructions() + 6 - ? _story->find_container_for(static_cast(_ptr - _story->instructions() - 6)) - : ~0U; - hash_t c_hash = (container_id != ~0U) ? _story->container_data(container_id)._hash : 0; - // if we are not at the start or terimanet bu we cannot name the current position it is not - // migratble + hash_t c_hash = 0; + if (_ptr != nullptr && _ptr >= _story->instructions() + 6) { + // Use find_migration_hash so that named-but-untracked containers (e.g. unlabeled choice + // bodies c-0, c-1 that are in _container_hash but not _container_map) are found correctly. + c_hash = _story->find_migration_hash(static_cast(_ptr - _story->instructions() - 6)); + } + // if we are not at the start or terminal but we cannot name the current position it is not + // migratable if (c_hash == 0 && _ptr != nullptr && _ptr != _story->instructions()) { return false; } @@ -678,12 +679,15 @@ size_t runner_impl::snap(unsigned char* data, snapper& snapper) const // This first field stores the hash of the container at the current position, // used by migration (story_impl::new_runner_from_snapshot) to navigate to the correct location. { - container_t container_id - = (_ptr != nullptr && _ptr >= _story->instructions() + 6) - ? _story->find_container_for(static_cast(_ptr - _story->instructions() - 6)) - : ~0U; - hash_t container_hash = (container_id != ~0U) ? _story->container_data(container_id)._hash : 0; - ptr = snap_write(ptr, container_hash, should_write); + hash_t container_hash = 0; + if (_ptr != nullptr && _ptr >= _story->instructions() + 6) { + // Use find_migration_hash so that named-but-untracked containers (e.g. unlabeled + // choice bodies c-0, c-1) that live in _container_hash but not _container_map are + // found via their exact start-offset, not approximated via their tracked parent. + container_hash + = _story->find_migration_hash(static_cast(_ptr - _story->instructions() - 6)); + } + ptr = snap_write(ptr, container_hash, should_write); } ptr = snap_write(ptr, offset, should_write); offset = _backup != nullptr ? _backup - _story->instructions() : 0; diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index 986a584f..6eaee86f 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -176,6 +176,24 @@ ip_t story_impl::find_offset_for(hash_t path) const return entry && entry->_hash == path ? _instruction_data + entry->_offset : nullptr; } +hash_t story_impl::find_migration_hash(uint32_t offset) const +{ + // Callers pass (_ptr - instructions - 6). For *tracked* containers jump() advances _ptr by 6 + // past the START_CONTAINER_MARKER, so (_ptr - 6) == marker offset == hash-entry offset. ✓ + // For *untracked* containers (no marker emitted, e.g. unlabeled choice bodies c-0, c-1) jump() + // does NOT advance _ptr, so (_ptr - 6) is 6 bytes BEFORE the container — the hash-entry offset + // is actually (_ptr - instructions) == (offset + 6). Try both. + for (uint32_t i = 0; i < _container_hash_size; ++i) { + if (_container_hash[i]._offset == offset || _container_hash[i]._offset == offset + 6) { + return _container_hash[i]._hash; + } + } + + // No named-container match: position is mid-content. Return the innermost tracked container. + container_t container_id = find_container_for(offset); + return (container_id != ~0U) ? container_data(container_id)._hash : 0; +} + globals story_impl::new_globals() { // create the new globals store diff --git a/inkcpp/story_impl.h b/inkcpp/story_impl.h index b4c6acfe..c7eb8f83 100644 --- a/inkcpp/story_impl.h +++ b/inkcpp/story_impl.h @@ -75,6 +75,12 @@ class story_impl : public story ip_t find_offset_for(hash_t path) const; + // Find the hash to use for migration at the given instruction offset. + // First tries an exact match in _container_hash (handles named but untracked containers such as + // unlabeled choice bodies c-0, c-1, etc.), then falls back to find_container_for for positions + // that are mid-content rather than at a container boundary. + hash_t find_migration_hash(uint32_t offset) const; + // Creates a new global store for use with runners executing this story virtual globals new_globals() override; virtual globals new_globals_from_snapshot(const snapshot&) override; diff --git a/inkcpp_python/pybind11 b/inkcpp_python/pybind11 index 0c69e1eb..1b499083 160000 --- a/inkcpp_python/pybind11 +++ b/inkcpp_python/pybind11 @@ -1 +1 @@ -Subproject commit 0c69e1eb2177fa8f8580632c7b1f97fdb606ce8f +Subproject commit 1b4990838904501de7110d27e96c0a4152029156 diff --git a/inkcpp_test/Migration.cpp b/inkcpp_test/Migration.cpp index 9b60899d..8db4f7e6 100644 --- a/inkcpp_test/Migration.cpp +++ b/inkcpp_test/Migration.cpp @@ -417,6 +417,16 @@ SCENARIO("Migratibility of the UE_example story", "[migration][intigration]") REQUIRE(thread->getall() == "You startk walking to Mansion.Entrance.\nJust in time you are able to see the door, someone with with a yellow summer dress enters it.\nYou're climbing the 56 steps up to the door; high tides are an annoying thing.\n"); REQUIRE(last_target == "Mansion.Entrance"); } + AND_WHEN("knock in v1") + { + REQUIRE(thread->getall() == "You startk walking to Mansion.Entrance.\nJust in time you are able to see the door, someone with with a yellow summer dress enters it.\nYou're climbing the 56 steps up to the door; high tides are an annoying thing.\n"); + REQUIRE(thread->num_choices() == 2); + thread->choose(1); + THEN("expect normal output") + { + REQUIRE(thread->getall() == "\"Ahh\", you cry while reaching for the door bell. Saying it was charched would be an understatement.\n"); + } + } } WHEN("go to mansion in v1, but get after choice text in v2") { @@ -432,9 +442,24 @@ SCENARIO("Migratibility of the UE_example story", "[migration][intigration]") thread2->bind("transition", callback, false); THEN("the new text at the same location should be displayed") { - REQUIRE(thread2->getall() == ""); + REQUIRE(thread2->getall() == "You startk walking to Mansion.Entrance.\nYou're climbing the 56 steps up to the door; high tides are an annoying thing.\n"); REQUIRE(last_target == "Mansion.Entrance"); } } + WHEN("knock in v1, but get after choice text in v2") + { + thread->getall(); + thread->choose(1); + thread->getall(); + thread->choose(1); + std::unique_ptr snap{thread->create_snapshot()}; + REQUIRE(snap->can_be_migrated()); + runner thread2 = story_v2->new_runner_from_snapshot(*snap); + thread2->bind("transition", callback, false); + THEN("the new text at the same location should be displayed") + { + REQUIRE(thread2->getall() == "\"Ahh\", you cry while reaching for the door bell. Saying it was charched would be an understatement.\n"); + } + } } } diff --git a/inkcpp_test/ink/UE_example_v2.ink b/inkcpp_test/ink/UE_example_v2.ink index 56c3c4dc..89feeab3 100644 --- a/inkcpp_test/ink/UE_example_v2.ink +++ b/inkcpp_test/ink/UE_example_v2.ink @@ -118,4 +118,4 @@ You're climbing the 56 steps up to the door; high tides are an annoying thing. + A ugly little creature, ignore it. - ->-> --> DONE \ No newline at end of file +-> DONE From bf63690a9afa1a83e4a02a8fdf60112921793940 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 8 Jun 2026 14:59:58 +0200 Subject: [PATCH 25/29] build(bump): add a script to auto bump inkcpp and ue version --- scripts/bump-version.py | 321 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 scripts/bump-version.py diff --git a/scripts/bump-version.py b/scripts/bump-version.py new file mode 100644 index 00000000..3ef0ad44 --- /dev/null +++ b/scripts/bump-version.py @@ -0,0 +1,321 @@ +#!/usr/bin/env python3 +"""bump-version.py — Bump the inkcpp project version or add a new Unreal Engine version. + +Usage: + bump-version.py inkcpp + bump-version.py ue + +Examples: + python scripts/bump-version.py inkcpp 0.1.11 + python scripts/bump-version.py ue 5.8 + +inkcpp subcommand updates: + - CMakeLists.txt (project(inkcpp VERSION ...)) + - setup.py (version="...") + +ue subcommand (adds new version, keeps all prior versions in CI): + - unreal/CMakeLists.txt (default INKCPP_UNREAL_TARGET_VERSION + comment) + - .github/workflows/build.yml (Install UE run block + new Upload step) + - .github/workflows/release.yml (download / zip / release artifact lists) + Also warns if Documentation/unreal/InkCPP_DEMO.zip targets a different UE version. +""" + +import json +import re +import sys +import zipfile + +from docopt import docopt +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +ARGS = docopt(__doc__, version="1.0") + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _ok(rel_path: str, detail: str = "") -> None: + suffix = f" ({detail})" if detail else "" + print(f" [OK] {rel_path}{suffix}") + + +def _warn(rel_path: str, msg: str) -> None: + print(f" [WARN] {rel_path} — {msg}") + + +def _info(msg: str) -> None: + print(f" [INFO] {msg}") + + +def _read(rel: str) -> tuple[Path, str]: + path = REPO_ROOT / rel + return path, path.read_text(encoding="utf-8") + + +def _write(path: Path, text: str) -> None: + path.write_text(text, encoding="utf-8") + + +# --------------------------------------------------------------------------- +# inkcpp version bump +# --------------------------------------------------------------------------- + +INKCPP_FILES = [ + ( + "CMakeLists.txt", + r"(project\(inkcpp VERSION\s+)\d+\.\d+\.\d+", + r"\g<1>{v}", + ), + ( + "setup.py", + r'(version=")[^"]+(")', + r"\g<1>{v}\2", + ), +] + + +def current_inkcpp_version() -> str: + _, text = _read("CMakeLists.txt") + m = re.search(r"project\(inkcpp VERSION\s+(\d+\.\d+\.\d+)", text) + if not m: + raise RuntimeError("Could not parse inkcpp version from CMakeLists.txt") + return m.group(1) + + +def bump_inkcpp(new_ver: str) -> None: + if not re.fullmatch(r"\d+\.\d+\.\d+", new_ver): + sys.exit(f"ERROR: inkcpp version must be MAJOR.MINOR.PATCH, got: {new_ver!r}") + + old_ver = current_inkcpp_version() + if old_ver == new_ver: + print(f"inkcpp version is already {old_ver} — nothing to do.") + return + + print(f"Bumping inkcpp {old_ver} → {new_ver}\n") + changed = [] + + for rel, pattern, tmpl in INKCPP_FILES: + path, original = _read(rel) + replacement = tmpl.replace("{v}", new_ver) + updated, count = re.subn(pattern, replacement, original) + if count == 0: + _warn(rel, "pattern not matched — skipped") + else: + _write(path, updated) + _ok(rel, f"{count} replacement{'s' if count != 1 else ''}") + changed.append(rel) + + print() + if changed: + files = " ".join(changed) + print("Suggested commit:") + print(f" git add {files}") + print(f' git commit -m "chore: bump inkcpp version {old_ver} → {new_ver}"') + + +# --------------------------------------------------------------------------- +# UE version bump +# --------------------------------------------------------------------------- + + +def current_ue_version() -> str: + _, text = _read("unreal/CMakeLists.txt") + m = re.search(r'set\(INKCPP_UNREAL_TARGET_VERSION\s+"(\d+\.\d+)"', text) + if not m: + raise RuntimeError("Could not parse UE version from unreal/CMakeLists.txt") + return m.group(1) + + +def _update_unreal_cmake(old_ue: str, new_ue: str) -> bool: + rel = "unreal/CMakeLists.txt" + path, text = _read(rel) + + # Update cached default value + text, n1 = re.subn( + r'(set\(INKCPP_UNREAL_TARGET_VERSION\s+")' + re.escape(old_ue) + r'"', + r"\g<1>" + new_ue + '"', + text, + ) + # Update the e.g. comment so it shows the previous default (old_ue) + text, n2 = re.subn( + r'(CACHE STRING "[^"]*e\.g: )[\d.]+(")', + r"\g<1>" + old_ue + r"\2", + text, + ) + + if n1 == 0: + _warn(rel, f"default version {old_ue!r} not found — skipped") + return False + _write(path, text) + _ok(rel, f"default {old_ue} → {new_ue}" + (", comment updated" if n2 else "")) + return True + + +def _update_build_yml(old_ue: str, new_ue: str) -> bool: + rel = ".github/workflows/build.yml" + path, text = _read(rel) + + old_u = old_ue.replace(".", "_") + new_u = new_ue.replace(".", "_") + + # Guard: already present? + if f'INKCPP_UNREAL_TARGET_VERSION="{new_ue}"' in text: + _warn(rel, f"UE {new_ue} already present — skipped") + return False + + # 1. Prepend new cmake build+install pair before the existing first pair + old_cmake_pair = ( + f' cmake $GITHUB_WORKSPACE -DINKCPP_UNREAL_TARGET_VERSION="{old_ue}" -DINKCPP_UNREAL=ON\n' + f" cmake --install . --config $BUILD_TYPE --prefix comp_unreal_{old_u} --component unreal" + ) + new_cmake_pair = ( + f' cmake $GITHUB_WORKSPACE -DINKCPP_UNREAL_TARGET_VERSION="{new_ue}" -DINKCPP_UNREAL=ON\n' + f" cmake --install . --config $BUILD_TYPE --prefix comp_unreal_{new_u} --component unreal\n" + ) + if old_cmake_pair not in text: + _warn( + rel, f"cmake install lines for {old_ue} not found — run block not updated" + ) + return False + text = text.replace(old_cmake_pair, new_cmake_pair + old_cmake_pair, 1) + + # 2. Insert new Upload step immediately before the existing Upload UE {old_ue} step + old_upload_anchor = f" - name: Upload UE {old_ue}\n" + new_upload_block = ( + f" - name: Upload UE {new_ue}\n" + f" if: ${{{{ matrix.unreal }}}}\n" + f" uses: actions/upload-artifact@v4\n" + f" with:\n" + f" name: unreal_{new_u}\n" + f" path: build/comp_unreal_{new_u}/\n" + ) + if old_upload_anchor not in text: + _warn(rel, f"'Upload UE {old_ue}' step not found — upload step not inserted") + return False + text = text.replace(old_upload_anchor, new_upload_block + old_upload_anchor, 1) + + _write(path, text) + _ok(rel, f"added cmake pair + Upload UE {new_ue} step") + return True + + +def _update_release_yml(old_ue: str, new_ue: str) -> bool: + rel = ".github/workflows/release.yml" + path, text = _read(rel) + + old_u = old_ue.replace(".", "_") + new_u = new_ue.replace(".", "_") + + # Guard: already present? + if f"unreal_{new_u}" in text: + _warn(rel, f"unreal_{new_u} already present — skipped") + return False + + ok = True + + # 1. Download step: -n unreal_OLD → -n unreal_NEW -n unreal_OLD + old_flag = f"-n unreal_{old_u}" + if old_flag not in text: + _warn(rel, f"download flag '-n unreal_{old_u}' not found") + ok = False + else: + text = text.replace(old_flag, f"-n unreal_{new_u} {old_flag}", 1) + + # 2. Zip for-loop: unreal_OLD → unreal_NEW unreal_OLD (only inside the for line) + for_pat = r"(for f in [^\n]*?)(" + re.escape(f"unreal_{old_u}") + r")" + text, n = re.subn(for_pat, r"\1unreal_" + new_u + r" \2", text, count=1) + if n == 0: + _warn(rel, f"unreal_{old_u} not found in zip for-loop") + ok = False + + # 3. gh release create asset list: "unreal_OLD.zip" → "unreal_NEW.zip" "unreal_OLD.zip" + old_asset = f'"unreal_{old_u}.zip"' + if old_asset not in text: + _warn(rel, f"release asset {old_asset} not found") + ok = False + else: + text = text.replace(old_asset, f'"unreal_{new_u}.zip" {old_asset}', 1) + + if not ok: + return False + + _write(path, text) + _ok(rel, "download / zip / release lists updated") + return True + + +def _check_demo_zip(new_ue: str) -> None: + zip_rel = "Documentation/unreal/InkCPP_DEMO.zip" + zip_path = REPO_ROOT / zip_rel + if not zip_path.exists(): + _info(f"{zip_rel} not found locally — skipping demo check") + return + + try: + with zipfile.ZipFile(zip_path) as zf: + uproject = next((n for n in zf.namelist() if n.endswith(".uproject")), None) + if not uproject: + _warn(zip_rel, "no .uproject file found inside the zip") + return + data = json.loads(zf.read(uproject).decode("utf-8")) + engine_assoc = data.get("EngineAssociation", "") + except Exception as exc: + _warn(zip_rel, f"could not inspect zip: {exc}") + return + + if engine_assoc == new_ue: + _ok(zip_rel, f"EngineAssociation already {engine_assoc!r}") + else: + print(f" [WARN] {zip_rel}") + print( + f" EngineAssociation is {engine_assoc!r} but new default is {new_ue!r}." + ) + print(f" Action: open the project in UE {new_ue}, let it convert,") + print(" re-export it, and replace the zip.") + + +def bump_ue(new_ue: str) -> None: + if not re.fullmatch(r"\d+\.\d+", new_ue): + sys.exit(f"ERROR: UE version must be MAJOR.MINOR (e.g. 5.8), got: {new_ue!r}") + + old_ue = current_ue_version() + if old_ue == new_ue: + print(f"UE default is already {old_ue} — nothing to do.") + return + + print(f"Adding UE {new_ue} (new default; {old_ue} and earlier remain in CI)\n") + + changed = [] + if _update_unreal_cmake(old_ue, new_ue): + changed.append("unreal/CMakeLists.txt") + if _update_build_yml(old_ue, new_ue): + changed.append(".github/workflows/build.yml") + if _update_release_yml(old_ue, new_ue): + changed.append(".github/workflows/release.yml") + + print() + print("Demo project check:") + _check_demo_zip(new_ue) + + if changed: + files = " ".join(changed) + print() + print("Suggested commit:") + print(f" git add {files}") + print( + f' git commit -m "chore: add UE {new_ue} support (default; {old_ue} still built)"' + ) + + +# --------------------------------------------------------------------------- +# Entry point +# --------------------------------------------------------------------------- + +if __name__ == "__main__": + if ARGS["inkcpp"]: + bump_inkcpp(ARGS[""]) + elif ARGS["ue"]: + bump_ue(ARGS[""]) From 29e506ebc97320c92cc8f7ce059a6371b6d07c3a Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 8 Jun 2026 23:42:57 +0200 Subject: [PATCH 26/29] fix(migration): runtimes fetch_tags did not stored heap strings, but story static strings --- inkcpp/runner_impl.cpp | 5 ++++- inkcpp_python/pybind11 | 2 +- inkcpp_test/Fixes.cpp | 22 ++++++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index e14d13fe..bb3bec2f 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -312,7 +312,9 @@ void runner_impl::fetch_tags(ip_t begin) iter += 6; continue; } - add_tag(read(iter + 6 + 2), tags_level::UNKNOWN); + // store tags in dynamic data, too keep migratable stories on the table + // TODO: maybe let tags live on the static data again. + add_tag(_globals->strings().duplicate(read(iter + 6 + 2)), tags_level::UNKNOWN); iter += 18; } } @@ -1651,6 +1653,7 @@ void runner_impl::step() ))); } break; case Command::TAG: { + inkFail("Command::TAG is Deprecated!"); read(); add_tag(read(), tags_level::UNKNOWN); } break; diff --git a/inkcpp_python/pybind11 b/inkcpp_python/pybind11 index 1b499083..0c69e1eb 160000 --- a/inkcpp_python/pybind11 +++ b/inkcpp_python/pybind11 @@ -1 +1 @@ -Subproject commit 1b4990838904501de7110d27e96c0a4152029156 +Subproject commit 0c69e1eb2177fa8f8580632c7b1f97fdb606ce8f diff --git a/inkcpp_test/Fixes.cpp b/inkcpp_test/Fixes.cpp index bd4f4c4e..ba1c5b01 100644 --- a/inkcpp_test/Fixes.cpp +++ b/inkcpp_test/Fixes.cpp @@ -2,6 +2,7 @@ #include "snapshot.h" #include "../snapshot_impl.h" +#include #include #include #include @@ -354,3 +355,24 @@ SCENARIO("Provoke thread array expension _ #142", "[regression][runtime]") } } } + +SCENARIO("Node text lookup error after loading story", "[regression][runtime][migration]") +{ + GIVEN("UE_example.ink and UE_example2.ink") + { + std::unique_ptr v1{story::from_file(INK_TEST_RESOURCE_DIR "UE_example.bin")}; + std::unique_ptr v2{story::from_file(INK_TEST_RESOURCE_DIR "UE_example_v2.bin")}; + WHEN("choosing a choice and v1, loading it in v2 and snap again") + { + runner thread = v1->new_runner(); + thread->getall(); + thread->choose(1); + std::unique_ptr snap{thread->create_snapshot()}; + runner thread_v2 = v2->new_runner_from_snapshot(*snap); + THEN("no assert when snapping again") + { + std::unique_ptr snap2{thread_v2->create_snapshot()}; + } + } + } +} From 1e100c9353b78586dd299853e7fec493b2ce4cd3 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 8 Jun 2026 23:49:34 +0200 Subject: [PATCH 27/29] docs(UE): update InkCPP-DEMO for new features --- Documentation/unreal/InkCPP_DEMO.zip | Bin 303895 -> 304068 bytes unreal/UE_example.ink | 31 +++++++++++++++++---------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Documentation/unreal/InkCPP_DEMO.zip b/Documentation/unreal/InkCPP_DEMO.zip index 8972ed105ef3b36d61a9eb35a27325f69b2f34a0..6174d1c7f2ec4b5994afeb77957125a2e3219e0e 100644 GIT binary patch delta 77311 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 f`*b-{Pa7YXXmg@PnV9pD4(D8qw5EWwijC50 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 S3; Mansion.Car @@ -31,24 +31,24 @@ You startk walking to {to}. = TTalkWithAnimals A potion which allows the consumer to talk with a variaty of animals. Just make sure your serroundings do not think you are crazy. - {can_talk_with_animals: Drinking more of it will not increase the effect.} + {StatusConditions ? (CanTalkWithAniamls): Drinking more of it will not increase the effect.} + [Put it Back] You put the potion back into your pouch. -> END - + {not can_talk_with_animals} [Drink] + + {not (StatusConditions ? CanTalkWithAniamls)} [Drink] + ~ StatusConditions += CanTalkWithAniamls A take a sip. The potion tastes like Hores, it is afull. - ~ can_talk_with_animals = true -> END = TInvisibility A potion which allows the consumer to stay unseen for the human eye. Not tested wtih other speccies. - {is_insible: Drinking more of it will not increase the effect.} + {StatusConditions ? (IsInvisible): Drinking more of it will not increase the effect.} + [Put it Back] You put the potion back into your pouch. -> END - + {not is_insible}[Drink] + + {not (StatusConditions ? (IsInvisible))}[Drink] + ~ StatusConditions += IsInvisible You put a small drop on your thoungh. It feels like liking a battery, such a nice feeling - ~ is_insible = true -> END === Faint @@ -63,6 +63,7 @@ No further action for today ... You step outside your car. Its a wired feeling beehing here again. -> Car_cycle = Car_cycle +# background:Car + (look_around)[look around # Type:Idle] It is a strange day. Despite it beeing spring, the sky is one massive gray soup. # style:Gray -> Car_cycle @@ -72,10 +73,14 @@ You step outside your car. Its a wired feeling beehing here again. = Entrance # background:Mansion -{not Mansion.look_around: Just in time you are able to see the door, someone with with a yellow summer dress enters it.} -You climbing the 56 steps up to the door, high water is a dump thing. +{not Mansion.look_around: + ~ Knowladge += YellowDress + Just in time you are able to see the door, someone with with a yellow summer dress enters it. +} +You're climbing the 56 steps up to the door; high tides are an annoying thing. -> Entrance_cycle = Entrance_cycle +# background:Mansion + (look_around) [look around # Type:Idle ] While watching around you, <> {Inventory hasnt Invisibility: @@ -89,8 +94,12 @@ You climbing the 56 steps up to the door, high water is a dump thing. You pick up the bottle and inspect it more. It is labeld "Invisible, just this one word written with and edding. -> Entrance_cycle + (knock)[Knock {knock: again?} # {knock: Type:Danger} ] - "Ahh", you cry while reaching for the door bell. Saying it was charched would be an understatement. ~ Health -= 20 + "Ahh", you cry while reaching for the door bell. Saying it was charched would be an understatement. { Health <= 0: -> Faint} -> Entrance_cycle ++ {knock && Knowladge ? (YellowDress)} [Inspect the Door] + You just saw someone enter, how did they do not get shoked? + It seems the developr run out of time, maybe in the next version we can continue? + -> Entrance_cycle -> DONE From 198121578b4486b3d660177f446359629b3cab6c Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 9 Jun 2026 10:31:30 +0200 Subject: [PATCH 28/29] style: formatting --- inkcpp/container_operations.cpp | 2 +- inkcpp/include/runner.h | 2 +- inkcpp/include/snapshot.h | 2 +- inkcpp/include/story.h | 2 +- inkcpp/list_impl.h | 5 +- inkcpp/snapshot_interface.h | 2 +- inkcpp/stack.h | 1 + inkcpp_c/include/inkcpp.h | 4 +- inkcpp_c/inkcpp.cpp | 4 +- inkcpp_python/pybind11 | 2 +- inkcpp_test/Observer.cpp | 82 +++++++++---------- .../Source/inkcpp/Private/InkRuntime.cpp | 40 ++++----- .../Source/inkcpp/Private/InkSnapshot.cpp | 4 +- .../Source/inkcpp/Private/InkThread.cpp | 8 +- .../inkcpp/Source/inkcpp/Private/InkVar.cpp | 6 +- .../inkcpp/Source/inkcpp/Public/InkRuntime.h | 7 +- 16 files changed, 85 insertions(+), 88 deletions(-) diff --git a/inkcpp/container_operations.cpp b/inkcpp/container_operations.cpp index 6ff5af0c..e632e4c6 100644 --- a/inkcpp/container_operations.cpp +++ b/inkcpp/container_operations.cpp @@ -40,7 +40,7 @@ void operation::operator()( basic_eval_stack& stack, value* vals ) { - (void)vals; + ( void ) vals; stack.push(value{}.set(static_cast(_runner.num_choices()))); } diff --git a/inkcpp/include/runner.h b/inkcpp/include/runner.h index 743d59a9..750c3227 100644 --- a/inkcpp/include/runner.h +++ b/inkcpp/include/runner.h @@ -36,7 +36,7 @@ class choice; class runner_interface { public: - virtual ~runner_interface() {}; + virtual ~runner_interface(){}; /** String type to simplify interfaces working with strings */ #ifdef INK_ENABLE_STL diff --git a/inkcpp/include/snapshot.h b/inkcpp/include/snapshot.h index ad5b7b18..4181031c 100644 --- a/inkcpp/include/snapshot.h +++ b/inkcpp/include/snapshot.h @@ -58,7 +58,7 @@ namespace ink::runtime class snapshot { public: - virtual ~snapshot() {}; + virtual ~snapshot(){}; /** Construct snapshot from blob. * Memory must be kept valid until the snapshot is deconstructed. diff --git a/inkcpp/include/story.h b/inkcpp/include/story.h index 68862a27..387cfb18 100644 --- a/inkcpp/include/story.h +++ b/inkcpp/include/story.h @@ -25,7 +25,7 @@ namespace ink::runtime class story { public: - virtual ~story() {}; + virtual ~story(){}; #pragma region Interface Methods /** * Creates a new global store diff --git a/inkcpp/list_impl.h b/inkcpp/list_impl.h index 82023c9a..0a5b9042 100644 --- a/inkcpp/list_impl.h +++ b/inkcpp/list_impl.h @@ -46,8 +46,7 @@ class list_impl final : public list_interface friend ink::runtime::internal::value; /// @todo wrong iteration order, first lists then flags - void next( - const char*& flag_name, const char*& list_name, int& i, bool one_list_only - ) const override; + void next(const char*& flag_name, const char*& list_name, int& i, bool one_list_only) + const override; }; } // namespace ink::runtime::internal diff --git a/inkcpp/snapshot_interface.h b/inkcpp/snapshot_interface.h index 3e8bd306..1cf9fb6e 100644 --- a/inkcpp/snapshot_interface.h +++ b/inkcpp/snapshot_interface.h @@ -23,7 +23,7 @@ class list_table; class snapshot_interface { public: - constexpr snapshot_interface() {}; + constexpr snapshot_interface(){}; static unsigned char* snap_write(unsigned char* ptr, const void* data, size_t length, bool write) { diff --git a/inkcpp/stack.h b/inkcpp/stack.h index e6a87b7a..5b383baf 100644 --- a/inkcpp/stack.h +++ b/inkcpp/stack.h @@ -34,6 +34,7 @@ namespace runtime class basic_stack : protected restorable { friend list_table; + protected: basic_stack(entry* data, size_t size); diff --git a/inkcpp_c/include/inkcpp.h b/inkcpp_c/include/inkcpp.h index 4b1ebba6..035cbae5 100644 --- a/inkcpp_c/include/inkcpp.h +++ b/inkcpp_c/include/inkcpp.h @@ -187,8 +187,8 @@ typedef struct HInkSTory HInkStory; #ifdef __GNUC__ #else # pragma warning(push) -// we use a anonymus union for convinence, feel free to change this in the future if problems should -// occure. + // we use a anonymus union for convinence, feel free to change this in the future if problems + // should occure. # pragma warning(disable : 4201) #endif /** Repserentation of a ink variable. diff --git a/inkcpp_c/inkcpp.cpp b/inkcpp_c/inkcpp.cpp index 2a33b6e0..15b4ff18 100644 --- a/inkcpp_c/inkcpp.cpp +++ b/inkcpp_c/inkcpp.cpp @@ -84,7 +84,9 @@ extern "C" { fseek(file, 0, SEEK_SET); unsigned char* data = static_cast(malloc(file_length)); inkAssert(data, "Malloc of size %u failed", file_length); - unsigned length = static_cast(fread(data, sizeof(unsigned char), static_cast(file_length), file)); + unsigned length = static_cast( + fread(data, sizeof(unsigned char), static_cast(file_length), file) + ); inkAssert( file_length == static_cast(length), "Expected to read file of size %u, but only read %u", file_length, length diff --git a/inkcpp_python/pybind11 b/inkcpp_python/pybind11 index 0c69e1eb..1b499083 160000 --- a/inkcpp_python/pybind11 +++ b/inkcpp_python/pybind11 @@ -1 +1 @@ -Subproject commit 0c69e1eb2177fa8f8580632c7b1f97fdb606ce8f +Subproject commit 1b4990838904501de7110d27e96c0a4152029156 diff --git a/inkcpp_test/Observer.cpp b/inkcpp_test/Observer.cpp index 91cd117c..d5fd05fd 100644 --- a/inkcpp_test/Observer.cpp +++ b/inkcpp_test/Observer.cpp @@ -36,19 +36,19 @@ SCENARIO("Observer", "[observer][globals][runtime]") int var1_cnt = 0; auto var1 = [&var1_cnt](int32_t i) { if (var1_cnt++ == 0) { - CHECK(i == 1); - } else { - CHECK(i == 5); - } + CHECK(i == 1); + } else { + CHECK(i == 5); + } }; int var2_cnt = 0; auto var2 = [&var2_cnt](const char* s) { std::string str(s); if (var2_cnt++ == 0) { - CHECK(str == "hello"); - } else { - CHECK(str == "test"); - } + CHECK(str == "hello"); + } else { + CHECK(str == "test"); + } }; globals->observe("var1", var1); @@ -102,10 +102,10 @@ SCENARIO("Observer", "[observer][globals][runtime]") int var1_cnt = 0; auto var1 = [&var1_cnt](int32_t i) { if (var1_cnt++ < 2) { - CHECK(i == 1); - } else { - CHECK(i == 5); - } + CHECK(i == 1); + } else { + CHECK(i == 5); + } }; globals->observe("var1", var1); globals->observe("var1", var1); @@ -195,13 +195,13 @@ SCENARIO("Observer", "[observer][globals][runtime]") auto var1 = [&var1_cnt, &globals](int32_t i) { ++var1_cnt; if (var1_cnt == 1) { - CHECK(i == 1); - } else if (var1_cnt == 2) { - CHECK(i == 5); - globals->set("var1", 8); - } else if (var1_cnt == 3) { - CHECK(i == 8); - } + CHECK(i == 1); + } else if (var1_cnt == 2) { + CHECK(i == 5); + globals->set("var1", 8); + } else if (var1_cnt == 3) { + CHECK(i == 8); + } }; globals->observe("var1", var1); std::string out = thread->getall(); @@ -220,13 +220,13 @@ SCENARIO("Observer", "[observer][globals][runtime]") auto var1 = [&var1_cnt, &globals](int32_t i) { ++var1_cnt; if (var1_cnt == 1) { - CHECK(i == 1); - globals->set("var1", 8); - } else if (var1_cnt == 2) { - CHECK(i == 8); - } else if (var1_cnt == 3) { - CHECK(i == 5); - } + CHECK(i == 1); + globals->set("var1", 8); + } else if (var1_cnt == 2) { + CHECK(i == 8); + } else if (var1_cnt == 3) { + CHECK(i == 5); + } }; globals->observe("var1", var1); std::string out = thread->getall(); @@ -245,16 +245,16 @@ SCENARIO("Observer", "[observer][globals][runtime]") auto var1 = [&var1_cnt, &globals](int32_t i) { ++var1_cnt; if (var1_cnt == 1) { - CHECK(i == 1); - globals->set("var1", 8); - } else if (var1_cnt == 2) { - CHECK(i == 8); - globals->set("var1", 10); - } else if (var1_cnt == 3) { - CHECK(i == 10); - } else if (var1_cnt == 4) { - CHECK(i == 5); - } + CHECK(i == 1); + globals->set("var1", 8); + } else if (var1_cnt == 2) { + CHECK(i == 8); + globals->set("var1", 10); + } else if (var1_cnt == 3) { + CHECK(i == 10); + } else if (var1_cnt == 4) { + CHECK(i == 5); + } }; globals->observe("var1", var1); std::string out = thread->getall(); @@ -272,11 +272,11 @@ SCENARIO("Observer", "[observer][globals][runtime]") int var1_cnt = 0; auto var1 = [&var1_cnt, &globals](int32_t i) { if (var1_cnt++ == 0) { - CHECK(i == 1); - } else { - CHECK(i == 5); - globals->set("var2", "didum"); - } + CHECK(i == 1); + } else { + CHECK(i == 5); + globals->set("var2", "didum"); + } }; int var2_cnt = 0; auto var2 = [&var2_cnt]() { diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index d2be5472..e36114a4 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -37,7 +37,8 @@ AInkRuntime::AInkRuntime() PrimaryActorTick.bCanEverTick = true; } -AInkRuntime::~AInkRuntime() { +AInkRuntime::~AInkRuntime() +{ if (mStableSnapshot.IsValid()) { mStableSnapshot->SetValue(FInkSnapshot()); mStableSnapshot.Reset(); @@ -197,23 +198,22 @@ FInkSnapshot AInkRuntime::Snapshot() TFuture AInkRuntime::MigratableSnapshot() { - // Fast path: already stable - FInkSnapshot snapshot = Snapshot(); + // Fast path: already stable + FInkSnapshot snapshot = Snapshot(); - if (snapshot.Migratable) - { - TPromise Immediate; - Immediate.SetValue(snapshot); - return Immediate.GetFuture(); - } + if (snapshot.Migratable) { + TPromise Immediate; + Immediate.SetValue(snapshot); + return Immediate.GetFuture(); + } - // Slow path: wait for stability - TSharedRef, ESPMode::ThreadSafe> Promise = - MakeShared, ESPMode::ThreadSafe>(); + // Slow path: wait for stability + TSharedRef, ESPMode::ThreadSafe> Promise + = MakeShared, ESPMode::ThreadSafe>(); - mStableSnapshot = Promise; + mStableSnapshot = Promise; - return Promise->GetFuture(); + return Promise->GetFuture(); } void AInkRuntime::RunnerEnterStableState(UInkThread* thread) @@ -249,10 +249,8 @@ void AInkRuntime::LoadSnapshot(const FInkSnapshot& snapshot) if (! mpSnapshot->can_be_migrated()) { UE_LOG( InkCpp, Error, - TEXT( - "Unable to load snapshot. The story has changed and the snapshot was taken at in " - "instable moment." - ) + TEXT("Unable to load snapshot. The story has changed and the snapshot was taken at in " + "instable moment.") ); } } @@ -288,10 +286,8 @@ UInkThread* if (mpSnapshot->num_runners() == mThreads.Num()) { UE_LOG( InkCpp, Warning, - TEXT( - "Already created all Threads from Snapshot!, will not create more. You can Still " - "create new Threads with entering the starting Path." - ) + TEXT("Already created all Threads from Snapshot!, will not create more. You can Still " + "create new Threads with entering the starting Path.") ); return nullptr; } diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkSnapshot.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkSnapshot.cpp index 80ba547b..385eab3a 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkSnapshot.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkSnapshot.cpp @@ -3,8 +3,8 @@ #include "Async/Async.h" -UInkMigratableSnapshotAsync* - UInkMigratableSnapshotAsync::GetMigratableSnapshot(AInkRuntime* Runtime) +UInkMigratableSnapshotAsync* UInkMigratableSnapshotAsync::GetMigratableSnapshot(AInkRuntime* Runtime +) { UInkMigratableSnapshotAsync* Node = NewObject(); Node->Runtime = Runtime; diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index 20b3782a..e39ad303 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -86,8 +86,8 @@ FInkHandle UInkThread::RegisterExternalFunction( const FString& functionName, const FExternalFunctionDelegate& function, bool lookaheadSafe ) { - TSharedPtr token = MakeShared(true); - uint32 nameHash = ink::hash_string(TCHAR_TO_UTF8(*functionName)); + TSharedPtr token = MakeShared(true); + uint32 nameHash = ink::hash_string(TCHAR_TO_UTF8(*functionName)); // If a previous binding exists for this name, invalidate it if (auto* prev = mExternalFunctionTokens.Find(nameHash)) { if (prev->IsValid()) { @@ -117,8 +117,8 @@ FInkHandle UInkThread::RegisterExternalEvent( const FString& functionName, const FExternalFunctionVoidDelegate& function, bool lookaheadSafe ) { - TSharedPtr token = MakeShared(true); - uint32 nameHash = ink::hash_string(TCHAR_TO_UTF8(*functionName)); + TSharedPtr token = MakeShared(true); + uint32 nameHash = ink::hash_string(TCHAR_TO_UTF8(*functionName)); if (auto* prev = mExternalFunctionTokens.Find(nameHash)) { if (prev->IsValid()) { **prev = false; diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp index d147b9dc..4b6032f9 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp @@ -25,10 +25,8 @@ FInkVar::FInkVar(ink::runtime::value val) case v_types::Uint32: UE_LOG( InkCpp, Warning, - TEXT( - "Converting uint to int, this will cause trouble if writing it back to ink (with " - "SetGlobalVariable)!" - ) + TEXT("Converting uint to int, this will cause trouble if writing it back to ink (with " + "SetGlobalVariable)!") ); IntVal = ( int32 ) val.get(); VarType = EInkVarType::Int; diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h index c9512047..a5098972 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h @@ -77,12 +77,13 @@ class INKCPP_API AInkRuntime : public AActor FInkSnapshot Snapshot(); /** creates a snapshot the next time the story is in a stable state. - * for Blueprints please use snapshotAsync::UInkMigratableSnapshotAsync() + * for Blueprints please use snapshotAsync::UInkMigratableSnapshotAsync() * This snapshot can be loaded with a new version of the same story. * can be loaded with @ref #LoadSnapshot() * * @attention typical this snapshot will be created after the next choice is taken. - * To archive this each active runner will yield after the next choice and only continue after the snapshot is taken. + * To archive this each active runner will yield after the next choice and only continue after the + * snapshot is taken. * */ TFuture MigratableSnapshot(); @@ -231,7 +232,7 @@ class INKCPP_API AInkRuntime : public AActor /** Active observer tokens. When Cancel() is called on the handle the token is set to false, * the lambda checks it before firing and skips. Tokens are cleaned up lazily. */ - TArray> mObserverTokens; + TArray> mObserverTokens; TSharedPtr> mStableSnapshot; UPROPERTY() TArray mYieldedThreadsForSnapshot; From 742755a79ecf44e6d1975c3e0fa1012a1a218007 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 9 Jun 2026 10:54:39 +0200 Subject: [PATCH 29/29] ci(docu): cache doxygen version --- .github/workflows/build.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8ff9d06c..7d8eb526 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -200,6 +200,8 @@ jobs: name: Build Doxygen documentation needs: [compilation, build-python] runs-on: ubuntu-latest + env: + DOXYGEN_VERSION: "1.17.0" steps: - uses: actions/checkout@v4 with: @@ -215,13 +217,19 @@ jobs: pybind11-stubgen pdoc --user + - uses: actions/cache@v5 + id: cache-doxygen + with: + path: doxygen-${{ env.DOXYGEN_VERSION }}.linux.bin.tar.gz + key: doxygen-${{ env.DOXYGEN_VERSION }} + - name: download Doxygen + if: steps.cache-doxygen.outputs.cache-hit != 'true' + run: wget https://www.doxygen.nl/files/doxygen-$DOXYGEN_VERSION.linux.bin.tar.gz - name: Install Doxygen run: | sudo apt-get install graphviz -y - wget https://www.doxygen.nl/files/doxygen-1.10.0.linux.bin.tar.gz - gunzip doxygen-*.tar.gz - tar xf doxygen-*.tar - cd doxygen-1.10.0/ + tar xzf doxygen-$DOXYGEN_VERSION.linux.bin.tar.gz + cd doxygen-$DOXYGEN_VERSION/ sudo make install - name: Setup cmake uses: jwlawson/actions-setup-cmake@v2