This commit is contained in:
Eauldane
2025-08-22 02:19:48 +01:00
commit a4c82452be
373 changed files with 52044 additions and 0 deletions

273
.editorconfig Normal file
View File

@@ -0,0 +1,273 @@
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
# C# files
[*.cs]
#### Core EditorConfig Options ####
# Indentation and spacing
indent_size = 4
indent_style = space
tab_width = 4
# New line preferences
end_of_line = crlf
insert_final_newline = false
#### .NET Coding Conventions ####
# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
file_header_template = unset
# this. and Me. preferences
dotnet_style_qualification_for_event = false
dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false
dotnet_style_qualification_for_property = false
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true
dotnet_style_predefined_type_for_member_access = true
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members
# Expression-level preferences
dotnet_style_coalesce_expression = true
dotnet_style_collection_initializer = true
dotnet_style_explicit_tuple_names = true
dotnet_style_namespace_match_folder = true
dotnet_style_null_propagation = true
dotnet_style_object_initializer = true
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true
dotnet_style_prefer_compound_assignment = true
dotnet_style_prefer_conditional_expression_over_assignment = true
dotnet_style_prefer_conditional_expression_over_return = true
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
dotnet_style_prefer_inferred_anonymous_type_member_names = true
dotnet_style_prefer_inferred_tuple_names = true
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
dotnet_style_prefer_simplified_boolean_expressions = true
dotnet_style_prefer_simplified_interpolation = true
# Field preferences
dotnet_style_readonly_field = true
# Parameter preferences
dotnet_code_quality_unused_parameters = all
# Suppression preferences
dotnet_remove_unnecessary_suppression_exclusions = 0
# New line preferences
dotnet_style_allow_multiple_blank_lines_experimental = true
dotnet_style_allow_statement_immediately_after_block_experimental = true
#### C# Coding Conventions ####
# var preferences
csharp_style_var_elsewhere = false
csharp_style_var_for_built_in_types = false
csharp_style_var_when_type_is_apparent = false
# Expression-bodied members
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true
csharp_style_pattern_matching_over_is_with_cast_check = true
csharp_style_prefer_extended_property_pattern = true
csharp_style_prefer_not_pattern = true
csharp_style_prefer_pattern_matching = true
csharp_style_prefer_switch_expression = true
# Null-checking preferences
csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_prefer_static_local_function = true:suggestion
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
csharp_style_prefer_readonly_struct = true:suggestion
# Code-block preferences
csharp_prefer_braces = true:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
# Expression-level preferences
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace:silent
# New line preferences
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
#### C# Formatting Rules ####
# New line preferences
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.private_or_internal_field_should_be_fieldstyle.severity = suggestion
dotnet_naming_rule.private_or_internal_field_should_be_fieldstyle.symbols = private_or_internal_field
dotnet_naming_rule.private_or_internal_field_should_be_fieldstyle.style = fieldstyle
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.fieldstyle.required_prefix = _
dotnet_naming_style.fieldstyle.required_suffix =
dotnet_naming_style.fieldstyle.word_separator =
dotnet_naming_style.fieldstyle.capitalization = camel_case
dotnet_diagnostic.MA0016.severity = silent
dotnet_diagnostic.MA0026.severity = warning
dotnet_diagnostic.MA0046.severity = suggestion
dotnet_diagnostic.MA0051.severity = suggestion
dotnet_diagnostic.MA0011.severity = suggestion
[*.{cs,vb}]
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
indent_size = 4
end_of_line = crlf
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
dotnet_style_readonly_field = true:suggestion
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
dotnet_style_allow_multiple_blank_lines_experimental = true:silent
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent

350
.gitignore vendored Normal file
View File

@@ -0,0 +1,350 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
.idea
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/

9
.gitmodules vendored Normal file
View File

@@ -0,0 +1,9 @@
[submodule "MareAPI"]
path = MareAPI
url = https://github.com/Eauldane/SnowcloakAPI.git
[submodule "Penumbra.Api"]
path = Penumbra.Api
url = https://github.com/Eauldane/Penumbra.Api.git
[submodule "Glamourer.Api"]
path = Glamourer.Api
url = https://github.com/Eauldane/Glamourer.Api.git

3625
Glamourer.Api/.editorconfig Normal file

File diff suppressed because it is too large Load Diff

3
Glamourer.Api/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
bin/
obj/
.vs/

View File

@@ -0,0 +1,14 @@
namespace Glamourer.Api.Api;
/// <summary> The full API available. </summary>
public interface IGlamourerApi : IGlamourerApiBase
{
/// <inheritdoc cref="IGlamourerApiDesigns"/>
public IGlamourerApiDesigns Designs { get; }
/// <inheritdoc cref="IGlamourerApiItems"/>
public IGlamourerApiItems Items { get; }
/// <inheritdoc cref="IGlamourerApiState"/>
public IGlamourerApiState State { get; }
}

View File

@@ -0,0 +1,11 @@
namespace Glamourer.Api.Api;
/// <summary> Basic API functions. </summary>
public interface IGlamourerApiBase
{
/// <summary>
/// Get the current API version of the Glamourer available in this installation.
/// Major version changes indicate incompatibilities, minor version changes are backward-compatible additions.
/// </summary>
public (int Major, int Minor) ApiVersion { get; }
}

View File

@@ -0,0 +1,33 @@
using Glamourer.Api.Enums;
namespace Glamourer.Api.Api;
/// <summary> All functions related to Glamourer designs. </summary>
public interface IGlamourerApiDesigns
{
/// <summary> Obtain a list of all available designs. </summary>
/// <returns> A dictionary of all designs from their GUID to their current display name. </returns>
public Dictionary<Guid, string> GetDesignList();
/// <summary> Apply an existing design to an actor. </summary>
/// <param name="designId"> The GUID of the design to apply. </param>
/// <param name="objectIndex"> The game object index of the actor to be manipulated. </param>
/// <param name="key"> A key to unlock or lock the state if necessary. </param>
/// <param name="flags"> The flags used for the reversion. Respects Once, Equipment, Customization, Lock (see <see cref="ApplyFlag"/>.)</param>
/// <returns> DesignNotFound, ActorNotFound, InvalidKey, Success. </returns>
public GlamourerApiEc ApplyDesign(Guid designId, int objectIndex, uint key, ApplyFlag flags);
/// <summary> Apply an existing design to an actor. </summary>
/// <param name="designId"> The GUID of the design to apply. </param>
/// <param name="playerName"> The name of the players to be manipulated. </param>
/// <param name="key"> A key to unlock or lock the state if necessary. </param>
/// <param name="flags"> The flags used for the reversion. Respects Once, Equipment, Customization, Lock (see <see cref="ApplyFlag"/>.)</param>
/// <returns> DesignNotFound, ActorNotFound, InvalidKey, Success. </returns>
/// /// <remarks>
/// The player does not have to be currently available as long as he has a persisted Glamourer state.<br/>
/// Only players are checked for name equality, no NPCs.<br/>
/// If multiple players of the same name are found, all of them are reverted.<br/>
/// Prefer to use the index-based function unless you need to get the state of someone currently unavailable.
/// </remarks>
public GlamourerApiEc ApplyDesignName(Guid designId, string playerName, uint key, ApplyFlag flags);
}

View File

@@ -0,0 +1,80 @@
using Glamourer.Api.Enums;
namespace Glamourer.Api.Api;
/// <summary> All functions related to items. </summary>
public interface IGlamourerApiItems
{
/// <summary> Set a single item on an actor. </summary>
/// <param name="objectIndex"> The game object index of the actor to be manipulated. </param>
/// <param name="slot"> The slot to apply the item to. </param>
/// <param name="itemId"> The (Custom) ID of the item to apply. </param>
/// <param name="stains"> The IDs of the stains to apply to the item. </param>
/// <param name="key"> A key to unlock or lock the state if necessary. </param>
/// <param name="flags"> The flags used for the reversion. Respects Once (see <see cref="ApplyFlag"/>.)</param>
/// <returns> ItemInvalid, ActorNotFound, ActorNotHuman, InvalidKey, Success. </returns>
/// <remarks> The item ID can be a custom item ID in Glamourer's format for models without an associated item, or a normal game item ID. </remarks>
public GlamourerApiEc SetItem(int objectIndex, ApiEquipSlot slot, ulong itemId, IReadOnlyList<byte> stains, uint key, ApplyFlag flags);
/// <summary> Set a single item on players. </summary>
/// <param name="playerName"> The name of the players to be manipulated. </param>
/// <param name="slot"> The slot to apply the item to. </param>
/// <param name="itemId"> The (Custom) ID of the item to apply. </param>
/// <param name="stains"> The IDs of the stains to apply to the item. </param>
/// <param name="key"> A key to unlock or lock the state if necessary. </param>
/// <param name="flags"> The flags used for the reversion. Respects Once (see <see cref="ApplyFlag"/>.)</param>
/// <returns> ItemInvalid, ActorNotFound, ActorNotHuman, InvalidKey, Success. </returns>
/// <remarks>
/// The item ID can be a custom item ID in Glamourer's format for models without an associated item, or a normal game item ID.<br/>
/// The player does not have to be currently available as long as he has a persisted Glamourer state.<br/>
/// Only players are checked for name equality, no NPCs.<br/>
/// If multiple players of the same name are found, all of them are modified.<br/>
/// Prefer to use the index-based function unless you need to get the state of someone currently unavailable.
/// </remarks>
public GlamourerApiEc SetItemName(string playerName, ApiEquipSlot slot, ulong itemId, IReadOnlyList<byte> stains, uint key,
ApplyFlag flags);
/// <summary> Set a single bonus item on an actor. </summary>
/// <param name="objectIndex"> The game object index of the actor to be manipulated. </param>
/// <param name="slot"> The bonus slot to apply the item to. </param>
/// <param name="bonusItemId"> The bonus item sheet ID of the item to apply (including stain). </param>
/// <param name="key"> A key to unlock or lock the state if necessary. </param>
/// <param name="flags"> The flags used for the reversion. Respects Once (see <see cref="ApplyFlag"/>.)</param>
/// <returns> ItemInvalid, ActorNotFound, ActorNotHuman, InvalidKey, Success. </returns>
/// <remarks> The bonus item ID can currently not be a custom item ID in Glamourer's format for models without an associated item. Use 0 to remove the bonus item. </remarks>
public GlamourerApiEc SetBonusItem(int objectIndex, ApiBonusSlot slot, ulong bonusItemId, uint key, ApplyFlag flags);
/// <summary> Set a single bonus item on an actor. </summary>
/// <param name="playerName"> The game object index of the actor to be manipulated. </param>
/// <param name="slot"> The bonus slot to apply the item to. </param>
/// <param name="bonusItemId"> The bonus item sheet ID of the item to apply (including stain). </param>
/// <param name="key"> A key to unlock or lock the state if necessary. </param>
/// <param name="flags"> The flags used for the reversion. Respects Once (see <see cref="ApplyFlag"/>.)</param>
/// <returns> ItemInvalid, ActorNotFound, ActorNotHuman, InvalidKey, Success. </returns>
/// <remarks>
/// The bonus item ID can currently not be a custom item ID in Glamourer's format for models without an associated item. Use 0 to remove the bonus item. <br/>
/// The player does not have to be currently available as long as he has a persisted Glamourer state.<br/>
/// Only players are checked for name equality, no NPCs.<br/>
/// If multiple players of the same name are found, all of them are modified.<br/>
/// Prefer to use the index-based function unless you need to get the state of someone currently unavailable.
/// </remarks>
public GlamourerApiEc SetBonusItemName(string playerName, ApiBonusSlot slot, ulong bonusItemId, uint key, ApplyFlag flags);
/// <summary> Set the defined Meta State flags to the active or inactive state on actor. </summary>
/// <param name="objectIndex"> The game object index of the actor to be manipulated. </param>
/// <param name="types"> The flags defining which meta states to update to the new value. This can be multiple at once. </param>
/// <param name="newValue"> The new value to update to. </param>
/// <param name="key"> A key to unlock or lock the state if necessary. </param>
/// <param name="flags"> The flags used for the reversion. Respects Once (see <see cref="ApplyFlag.Once"/>.)</param>
/// <returns> ItemInvalid, ActorNotFound, ActorNotHuman, InvalidKey, Success. </returns>
public GlamourerApiEc SetMetaState(int objectIndex, MetaFlag types, bool newValue, uint key, ApplyFlag flags);
/// <summary> Set the defined Meta State flags to the active or inactive state on actor (by name) </summary>
/// <param name="playerName"> The name of the players to be manipulated. </param>
/// <param name="types"> The flags defining which meta states to update to the new value. This can be multiple at once. </param>
/// <param name="newValue"> The new value to update to. </param>
/// <param name="key"> A key to unlock or lock the state if necessary. </param>
/// <param name="flags"> The flags used for the reversion. Respects Once (see <see cref="ApplyFlag.Once"/>.)</param>
/// <returns> ItemInvalid, ActorNotFound, ActorNotHuman, InvalidKey, Success. </returns>
public GlamourerApiEc SetMetaStateName(string playerName, MetaFlag types, bool newValue, uint key, ApplyFlag flags);
}

View File

@@ -0,0 +1,124 @@
using Glamourer.Api.Enums;
using Newtonsoft.Json.Linq;
namespace Glamourer.Api.Api;
/// <summary> Any functions related to Glamourer's state tracking. </summary>
public interface IGlamourerApiState
{
/// <summary> Get the current Glamourer state of an actor. </summary>
/// <param name="objectIndex"> The game object index of the desired actor. </param>
/// <param name="key"> A key to unlock the state if necessary. </param>
/// <returns> ActorNotFound, InvalidKey or Success, and the state on success. </returns>
/// <remarks> The actor does not need to have a prior Glamourer state as long as it can be found. </remarks>
public (GlamourerApiEc, JObject?) GetState(int objectIndex, uint key);
/// <summary> Get the current Glamourer state of a player character. </summary>
/// <param name="playerName"> The name of the desired player. </param>
/// <param name="key"> A key to unlock the state if necessary. </param>
/// <returns> ActorNotFound, InvalidKey or Success, and the state on success. </returns>
/// <remarks>
/// The player does not have to be currently available as long as he has a persisted Glamourer state.
/// Only players are checked for name equality, no NPCs.
/// If multiple players of the same name are found, the first is returned.
/// Prefer to use the index-based function unless you need to get the state of someone currently unavailable.
/// </remarks>
public (GlamourerApiEc, JObject?) GetStateName(string playerName, uint key);
/// <inheritdoc cref="GetState"/>
public (GlamourerApiEc, string?) GetStateBase64(int objectIndex, uint key);
/// <inheritdoc cref="GetStateName"/>
public (GlamourerApiEc, string?) GetStateBase64Name(string objectName, uint key);
/// <summary> Apply a supplied state to an actor. </summary>
/// <param name="applyState"> The state, which can be either a Glamourer-supplied JObject or a Base64 string. </param>
/// <param name="objectIndex"> The game object index of the actor to be manipulated. </param>
/// <param name="key"> A key to unlock or lock the state if necessary. </param>
/// <param name="flags"> The flags used for the application. Respects Once, Equipment, Customization and Lock (see <see cref="ApplyFlag"/>.) </param>
/// <returns> ActorNotFound, InvalidKey, ActorNotHuman, Success. </returns>
public GlamourerApiEc ApplyState(object applyState, int objectIndex, uint key, ApplyFlag flags);
/// <summary> Apply a supplied state to players. </summary>
/// <param name="applyState"> The state, which can be either a Glamourer-supplied JObject or a Base64 string. </param>
/// <param name="playerName"> The name of the player to be manipulated. </param>
/// <param name="key"> A key to unlock or lock the state if necessary. </param>
/// <param name="flags"> The flags used for the application. Respects Once, Equipment, Customization and Lock (see <see cref="ApplyFlag"/>.) </param>
/// <returns> ActorNotFound, InvalidKey, ActorNotHuman, Success. </returns>
/// <remarks>
/// The player does not have to be currently available as long as he has a persisted Glamourer state.<br/>
/// Only players are checked for name equality, no NPCs.<br/>
/// If multiple players of the same name are found, all of them are manipulated.<br/>
/// Prefer to use the index-based function unless you need to get the state of someone currently unavailable.
/// </remarks>
public GlamourerApiEc ApplyStateName(object applyState, string playerName, uint key, ApplyFlag flags);
/// <summary> Revert the Glamourer state of an actor to Game state. </summary>
/// <param name="objectIndex"> The game object index of the actor to be manipulated. </param>
/// <param name="key"> A key to unlock the state if necessary. </param>
/// <param name="flags"> The flags used for the reversion. Respects Equipment and Customization (see <see cref="ApplyFlag"/>.) </param>
/// <returns> ActorNotFound, InvalidKey, Success, NothingDone. </returns>
public GlamourerApiEc RevertState(int objectIndex, uint key, ApplyFlag flags);
/// <summary> Revert the Glamourer state of players to game state. </summary>
/// <param name="playerName"> The name of the players to be reverted. </param>
/// <param name="key"> A key to unlock the state if necessary. </param>
/// <param name="flags"> The flags used for the reversion. Respects Equipment and Customization (see <see cref="ApplyFlag"/>.) </param>
/// <returns> ActorNotFound, InvalidKey, Success, NothingDone. </returns>
/// /// <remarks>
/// The player does not have to be currently available as long as he has a persisted Glamourer state.<br/>
/// Only players are checked for name equality, no NPCs.<br/>
/// If multiple players of the same name are found, all of them are reverted.<br/>
/// Prefer to use the index-based function unless you need to get the state of someone currently unavailable.
/// </remarks>
public GlamourerApiEc RevertStateName(string playerName, uint key, ApplyFlag flags);
/// <summary> Unlock the Glamourer state of an actor with a key. </summary>
/// <param name="objectIndex"> The game object index of the actor to be manipulated. </param>
/// <param name="key"> A key to unlock the state. </param>
/// <returns> ActorNotFound, InvalidKey, Success, NothingDone. </returns>
public GlamourerApiEc UnlockState(int objectIndex, uint key);
/// <summary> Unlock the Glamourer state of players with a key. </summary>
/// <param name="playerName"> The name of the players to be unlocked. </param>
/// <param name="key"> A key to unlock the state. </param>
/// <returns> InvalidKey, Success, NothingDone. </returns>
public GlamourerApiEc UnlockStateName(string playerName, uint key);
/// <summary> Unlock all active glamourer states with a key. </summary>
/// <param name="key"> The key to unlock states with. </param>
/// <returns> The number of unlocked states. </returns>
public int UnlockAll(uint key);
/// <summary> Revert the Glamourer state of an actor to automation state. </summary>
/// <param name="objectIndex"> The game object index of the actor to be manipulated. </param>
/// <param name="key"> A key to unlock the state if necessary. </param>
/// <param name="flags"> The flags used for the reversion. Respects Once and Lock (see <see cref="ApplyFlag"/>.) </param>
/// <returns> ActorNotFound, InvalidKey, Success, NothingDone. </returns>
public GlamourerApiEc RevertToAutomation(int objectIndex, uint key, ApplyFlag flags);
/// <summary> Revert the Glamourer state of players to automation state. </summary>
/// <param name="playerName"> The name of the players to be reverted. </param>
/// <param name="key"> A key to unlock the state if necessary. </param>
/// <param name="flags"> The flags used for the reversion. Respects Once and Lock (see <see cref="ApplyFlag"/>.) </param>
/// <returns> ActorNotFound, InvalidKey, Success, NothingDone. </returns>
/// /// <remarks>
/// The player does not have to be currently available as long as he has a persisted Glamourer state.<br/>
/// Only players are checked for name equality, no NPCs.<br/>
/// If multiple players of the same name are found, all of them are reverted.<br/>
/// Prefer to use the index-based function unless you need to get the state of someone currently unavailable.
/// </remarks>
public GlamourerApiEc RevertToAutomationName(string playerName, uint key, ApplyFlag flags);
/// <summary> Invoked with the game object pointer (if available) whenever an actors tracked state changes. </summary>
public event Action<nint> StateChanged;
/// <summary> Invoked with the game object pointer (if available) whenever an actors tracked state changes, with the type of change. </summary>
public event Action<nint, StateChangeType> StateChangedWithType;
/// <summary> Invoked with the game object pointer (if available) whenever an actors tracked state finalizes a grouped change consisting of multiple smaller changes. </summary>
public event Action<nint, StateFinalizationType> StateFinalized;
/// <summary> Invoked when the player enters or leaves GPose (true => entered GPose, false => left GPose). </summary>
public event Action<bool>? GPoseChanged;
}

View File

@@ -0,0 +1,11 @@
namespace Glamourer.Api.Enums;
/// <summary> Bonus item slots restricted to API-relevant slots. </summary>
public enum ApiBonusSlot : byte
{
/// <summary> No slot. </summary>
Unknown = 0,
/// <summary> The Glasses slot. </summary>
Glasses = 1,
}

View File

@@ -0,0 +1,45 @@
namespace Glamourer.Api.Enums;
/// <summary> Equip slots restricted to API-relevant slots, but compatible with GameData.EquipSlots. </summary>
public enum ApiEquipSlot : byte
{
/// <summary> No slot. </summary>
Unknown = 0,
/// <summary> Mainhand, also used for both-handed weapons. </summary>
MainHand = 1,
/// <summary> Offhand, used for shields or if you want to apply the offhand component of certain weapons. </summary>
OffHand = 2,
/// <summary> Head. </summary>
Head = 3,
/// <summary> Body. </summary>
Body = 4,
/// <summary> Hands. </summary>
Hands = 5,
/// <summary> Legs. </summary>
Legs = 7,
/// <summary> Feet. </summary>
Feet = 8,
/// <summary> Ears. </summary>
Ears = 9,
/// <summary> Neck. </summary>
Neck = 10,
/// <summary> Wrists. </summary>
Wrists = 11,
/// <summary> Right Finger. </summary>
RFinger = 12,
/// <summary> Left Finger. </summary>
/// <remarks> Not officially existing, means "weapon could be equipped in either hand" for the game. </remarks>
LFinger = 14,
}

View File

@@ -0,0 +1,31 @@
namespace Glamourer.Api.Enums;
/// <summary> Application flags that can be used in different situations. </summary>
[Flags]
public enum ApplyFlag : ulong
{
/// <summary> Apply the selected manipulation only once, without forcing the state into automation. </summary>
Once = 0x01,
/// <summary> Apply the selected manipulation on the equipment (might be more or less supported). </summary>
Equipment = 0x02,
/// <summary> Apply the selected manipulation on the customizations (might be more or less supported). </summary>
Customization = 0x04,
/// <summary> Lock the state with the given key after applying the selected manipulation </summary>
Lock = 0x08,
}
/// <summary> Extensions for apply flags. </summary>
public static class ApplyFlagEx
{
/// <summary> The default application flags for design-based manipulations. </summary>
public const ApplyFlag DesignDefault = ApplyFlag.Once | ApplyFlag.Equipment | ApplyFlag.Customization;
/// <summary> The default application flags for state-based manipulations. </summary>
public const ApplyFlag StateDefault = ApplyFlag.Equipment | ApplyFlag.Customization | ApplyFlag.Lock;
/// <summary> The default application flags for reverse manipulations. </summary>
public const ApplyFlag RevertDefault = ApplyFlag.Equipment | ApplyFlag.Customization;
}

View File

@@ -0,0 +1,29 @@
namespace Glamourer.Api.Enums;
/// <summary> Return codes for API functions. </summary>
public enum GlamourerApiEc
{
/// <summary> The function succeeded. </summary>
Success = 0,
/// <summary> The function did not encounter a problem, but also did not do anything. </summary>
NothingDone = 1,
/// <summary> The requested actor was not found. </summary>
ActorNotFound = 2,
/// <summary> The requested actor was not human, but should have been. </summary>
ActorNotHuman,
/// <summary> The requested design was not found. </summary>
DesignNotFound,
/// <summary> The requested item was not found or could not be applied to the requested slot. </summary>
ItemInvalid,
/// <summary> The state of an actor could not be manipulated because it was locked and the provided key could not unlock it. </summary>
InvalidKey,
/// <summary> The provided object could not be converted into a valid Glamourer state to apply. </summary>
InvalidState,
}

View File

@@ -0,0 +1,11 @@
namespace Glamourer.Api.Enums;
/// <summary> Application flags for setting the meta state of an actor. </summary>
[Flags]
public enum MetaFlag : ulong
{
Wetness = 0x01,
HatState = 0x02,
VisorState = 0x04,
WeaponState = 0x08,
}

View File

@@ -0,0 +1,47 @@
namespace Glamourer.Api.Enums;
/// <summary> What type of information changed in a state. </summary>
public enum StateChangeType
{
/// <summary> A characters saved state had the model id changed. This means everything may have changed. </summary>
Model = 0,
/// <summary> A characters saved state had multiple customization values changed. </summary>
EntireCustomize = 1,
/// <summary> A characters saved state had a customization value changed. </summary>
Customize = 2,
/// <summary> A characters saved state had an equipment piece changed. </summary>
Equip = 3,
/// <summary> A characters saved state had its weapons changed. </summary>
Weapon = 4,
/// <summary> A characters saved state had a stain changed. </summary>
Stains = 5,
/// <summary> A characters saved state had a crest visibility changed. </summary>
Crest = 6,
/// <summary> A characters saved state had its customize parameter changed. </summary>
Parameter = 7,
/// <summary> A characters saved state had a material color table value changed. </summary>
MaterialValue = 8,
/// <summary> A characters saved state had a design applied. This means everything may have changed. </summary>
Design = 9,
/// <summary> A characters saved state had its state reset to its game values. </summary>
Reset = 10,
/// <summary> A characters saved state had a meta toggle changed. </summary>
Other = 11,
/// <summary> A characters state was reapplied. Data is null. </summary>
Reapply = 12,
/// <summary> A characters saved state had a bonus item changed. </summary>
BonusItem = 13,
}

View File

@@ -0,0 +1,36 @@
namespace Glamourer.Api.Enums;
/// <summary> What type of Glamourer process was performed on the actors state to update it. </summary>
public enum StateFinalizationType
{
/// <summary> A characters saved state had the model id altered. </summary>
ModelChange = 0,
/// <summary> A singular Design was applied to an actors state. </summary>
DesignApplied = 1,
/// <summary> A characters saved state had been reset to game values. </summary>
Revert = 2,
/// <summary> A characters saved state had only its customization data reset to game state. </summary>
RevertCustomize = 3,
/// <summary> A characters saved state had only its equipment data reset to game state. </summary>
RevertEquipment = 4,
/// <summary> A characters saved state had its advanced values reverted to game state. </summary>
RevertAdvanced = 5,
/// <summary> A characters saved state was reverted to automation state on top of their game state </summary>
RevertAutomation = 6,
/// <summary> A characters saved state had a generic reapply as a single operation. </summary>
Reapply = 7,
/// <summary> A characters saved state had their automation state reapplied over their existing state. </summary>
ReapplyAutomation = 8,
/// <summary> A characters save state finished applying all updated slots for game state on gearset change or initial load. </summary>
Gearset = 9,
}

View File

@@ -0,0 +1,34 @@
<Project Sdk="Dalamud.NET.Sdk/13.0.0">
<PropertyGroup>
<AssemblyTitle>Glamourer.Api</AssemblyTitle>
<Product>Glamourer</Product>
<Copyright>Copyright © 2025</Copyright>
<FileVersion>2.4.1.0</FileVersion>
<AssemblyVersion>2.4.1.0</AssemblyVersion>
<PackageVersion>2.4.1</PackageVersion>
<PackageReadmeFile>README.md</PackageReadmeFile>
<OutputPath>bin\$(Configuration)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Title>Glamourer.Api</Title>
<Authors>Ottermandias</Authors>
<RepositoryUrl>https://github.com/Ottermandias/Glamourer</RepositoryUrl>
<Description>Auxiliary functions for Glamourers external API.</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<PropertyGroup>
<Use_DalamudPackager>false</Use_DalamudPackager>
</PropertyGroup>
<PropertyGroup>
<NoWarn>1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ipc/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -0,0 +1,4 @@
// Global using directives
global using System;
global using System.Collections.Generic;

View File

@@ -0,0 +1,114 @@
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
namespace Glamourer.Api.Helpers;
/// <summary>
/// Specialized subscriber only allowing to invoke actions.
/// </summary>
public class ActionSubscriber
{
private readonly ICallGateSubscriber<object?>? _subscriber;
/// <summary> Whether the subscriber could successfully be created. </summary>
public bool Valid
=> _subscriber != null;
protected ActionSubscriber(IDalamudPluginInterface pi, string label)
{
try
{
_subscriber = pi.GetIpcSubscriber<object?>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <summary> Invoke the action. See the source of the subscriber for details.</summary>
protected void Invoke()
=> _subscriber?.InvokeAction();
}
/// <inheritdoc cref="ActionSubscriber"/>
public class ActionSubscriber<T1>
{
private readonly ICallGateSubscriber<T1, object?>? _subscriber;
/// <summary> Whether the subscriber could successfully be created. </summary>
public bool Valid
=> _subscriber != null;
protected ActionSubscriber(IDalamudPluginInterface pi, string label)
{
try
{
_subscriber = pi.GetIpcSubscriber<T1, object?>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <summary> Invoke the action. See the source of the subscriber for details.</summary>
protected void Invoke(T1 a)
=> _subscriber?.InvokeAction(a);
}
/// <inheritdoc cref="ActionSubscriber"/>
public class ActionSubscriber<T1, T2>
{
private readonly ICallGateSubscriber<T1, T2, object?>? _subscriber;
/// <inheritdoc cref="ActionSubscriber{T1}.Valid"/>
public bool Valid
=> _subscriber != null;
protected ActionSubscriber(IDalamudPluginInterface pi, string label)
{
try
{
_subscriber = pi.GetIpcSubscriber<T1, T2, object?>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <inheritdoc cref="ActionSubscriber.Invoke"/>
protected void Invoke(T1 a, T2 b)
=> _subscriber?.InvokeAction(a, b);
}
/// <inheritdoc cref="ActionSubscriber"/>
public class ActionSubscriber<T1, T2, T3>
{
private readonly ICallGateSubscriber<T1, T2, T3, object?>? _subscriber;
/// <inheritdoc cref="ActionSubscriber{T1}.Valid"/>
public bool Valid
=> _subscriber != null;
protected ActionSubscriber(IDalamudPluginInterface pi, string label)
{
try
{
_subscriber = pi.GetIpcSubscriber<T1, T2, T3, object?>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <inheritdoc cref="ActionSubscriber.Invoke"/>
protected void Invoke(T1 a, T2 b, T3 c)
=> _subscriber?.InvokeAction(a, b, c);
}

View File

@@ -0,0 +1,234 @@
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Services;
namespace Glamourer.Api.Helpers;
/// <summary>
/// Specialized disposable Provider for Events.<para />
/// Will execute the unsubscriber action on dispose if any is provided.<para />
/// Can only be invoked and disposed.
/// </summary>
public sealed class EventProvider : IDisposable
{
private readonly IPluginLog _log;
private ICallGateProvider<object?>? _provider;
private Delegate? _unsubscriber;
public EventProvider(IDalamudPluginInterface pi, string label, (Action<Action> Add, Action<Action> Del)? subscribe = null)
{
_unsubscriber = null;
_log = PluginLogHelper.GetLog(pi);
try
{
_provider = pi.GetIpcProvider<object?>(label);
subscribe?.Add(Invoke);
_unsubscriber = subscribe?.Del;
}
catch (Exception e)
{
_log.Error($"Error registering IPC Provider for {label}\n{e}");
_provider = null;
}
}
public EventProvider(IDalamudPluginInterface pi, string label, Action<EventProvider> add, Action<EventProvider> del)
{
_unsubscriber = null;
_log = PluginLogHelper.GetLog(pi);
try
{
_provider = pi.GetIpcProvider<object?>(label);
add(this);
_unsubscriber = del;
}
catch (Exception e)
{
_log.Error($"Error registering IPC Provider for {label}\n{e}");
_provider = null;
}
}
/// <summary> Invoke the event.</summary>
public void Invoke()
{
try
{
_provider?.SendMessage();
}
catch (Exception e)
{
_log.Error($"Exception thrown on IPC event:\n{e}");
}
}
public void Dispose()
{
switch (_unsubscriber)
{
case Action<Action> a:
a(Invoke);
break;
case Action<EventProvider> b:
b(this);
break;
}
_unsubscriber = null;
_provider = null;
GC.SuppressFinalize(this);
}
~EventProvider()
=> Dispose();
}
/// <inheritdoc cref="EventProvider"/>
public sealed class EventProvider<T1> : IDisposable
{
private readonly IPluginLog _log;
private ICallGateProvider<T1, object?>? _provider;
private Delegate? _unsubscriber;
public EventProvider(IDalamudPluginInterface pi, string label, (Action<Action<T1>> Add, Action<Action<T1>> Del)? subscribe = null)
{
_unsubscriber = null;
_log = PluginLogHelper.GetLog(pi);
try
{
_provider = pi.GetIpcProvider<T1, object?>(label);
subscribe?.Add(Invoke);
_unsubscriber = subscribe?.Del;
}
catch (Exception e)
{
_log.Error($"Error registering IPC Provider for {label}\n{e}");
_provider = null;
}
}
public EventProvider(IDalamudPluginInterface pi, string label, Action<EventProvider<T1>> add, Action<EventProvider<T1>> del)
{
_unsubscriber = null;
_log = PluginLogHelper.GetLog(pi);
try
{
_provider = pi.GetIpcProvider<T1, object?>(label);
add(this);
_unsubscriber = del;
}
catch (Exception e)
{
_log.Error($"Error registering IPC Provider for {label}\n{e}");
_provider = null;
}
}
/// <inheritdoc cref="EventProvider.Invoke"/>
public void Invoke(T1 a)
{
try
{
_provider?.SendMessage(a);
}
catch (Exception e)
{
_log.Error($"Exception thrown on IPC event:\n{e}");
}
}
public void Dispose()
{
switch (_unsubscriber)
{
case Action<Action<T1>> a:
a(Invoke);
break;
case Action<EventProvider<T1>> b:
b(this);
break;
}
_unsubscriber = null;
_provider = null;
GC.SuppressFinalize(this);
}
~EventProvider()
=> Dispose();
}
/// <inheritdoc cref="EventProvider"/>
public sealed class EventProvider<T1, T2> : IDisposable
{
private readonly IPluginLog _log;
private ICallGateProvider<T1, T2, object?>? _provider;
private Delegate? _unsubscriber;
public EventProvider(IDalamudPluginInterface pi, string label, (Action<Action<T1, T2>> Add, Action<Action<T1, T2>> Del)? subscribe = null)
{
_unsubscriber = null;
_log = PluginLogHelper.GetLog(pi);
try
{
_provider = pi.GetIpcProvider<T1, T2, object?>(label);
subscribe?.Add(Invoke);
_unsubscriber = subscribe?.Del;
}
catch (Exception e)
{
_log.Error($"Error registering IPC Provider for {label}\n{e}");
_provider = null;
}
}
public EventProvider(IDalamudPluginInterface pi, string label, Action<EventProvider<T1, T2>> add, Action<EventProvider<T1, T2>> del)
{
_unsubscriber = null;
_log = PluginLogHelper.GetLog(pi);
try
{
_provider = pi.GetIpcProvider<T1, T2, object?>(label);
add(this);
_unsubscriber = del;
}
catch (Exception e)
{
_log.Error($"Error registering IPC Provider for {label}\n{e}");
_provider = null;
}
}
/// <inheritdoc cref="EventProvider.Invoke"/>
public void Invoke(T1 a, T2 b)
{
try
{
_provider?.SendMessage(a, b);
}
catch (Exception e)
{
_log.Error($"Exception thrown on IPC event:\n{e}");
}
}
public void Dispose()
{
switch (_unsubscriber)
{
case Action<Action<T1, T2>> a:
a(Invoke);
break;
case Action<EventProvider<T1, T2>> b:
b(this);
break;
}
_unsubscriber = null;
_provider = null;
GC.SuppressFinalize(this);
}
~EventProvider()
=> Dispose();
}

View File

@@ -0,0 +1,394 @@
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Services;
namespace Glamourer.Api.Helpers;
/// <summary>
/// Specialized disposable Subscriber for Events.<para />
/// Subscriptions are wrapped to be individually exception-safe.<para/>
/// Can be enabled and disabled.<para/>
/// </summary>
public sealed class EventSubscriber : IDisposable
{
private readonly string _label;
private readonly IPluginLog _log;
private readonly Dictionary<Action, Action> _delegates = new();
private ICallGateSubscriber<object?>? _subscriber;
private bool _disabled;
public EventSubscriber(IDalamudPluginInterface pi, string label, params Action[] actions)
{
_label = label;
_log = PluginLogHelper.GetLog(pi);
try
{
_subscriber = pi.GetIpcSubscriber<object?>(label);
foreach (var action in actions)
Event += action;
_disabled = false;
}
catch (Exception e)
{
_log.Error($"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <summary>
/// Enable all currently subscribed actions registered with this EventSubscriber.
/// Does nothing if it is already enabled.
/// </summary>
public void Enable()
{
if (_disabled && _subscriber != null)
{
foreach (var action in _delegates.Values)
_subscriber.Subscribe(action);
_disabled = false;
}
}
/// <summary>
/// Disable all subscribed actions registered with this EventSubscriber.
/// Does nothing if it is already disabled.
/// Does not forget the actions, only disables them.
/// </summary>
public void Disable()
{
if (!_disabled)
{
if (_subscriber != null)
foreach (var action in _delegates.Values)
_subscriber.Unsubscribe(action);
_disabled = true;
}
}
/// <summary>
/// Add or remove an action to the IPC event, if it is valid.
/// </summary>
public event Action Event
{
add
{
if (_subscriber != null && !_delegates.ContainsKey(value))
{
void Action()
{
try
{
value();
}
catch (Exception e)
{
_log.Error($"Exception invoking IPC event {_label}:\n{e}");
}
}
if (_delegates.TryAdd(value, Action) && !_disabled)
_subscriber.Subscribe(Action);
}
}
remove
{
if (_subscriber != null && _delegates.Remove(value, out var action))
_subscriber.Unsubscribe(action);
}
}
public void Dispose()
{
Disable();
_subscriber = null;
_delegates.Clear();
}
~EventSubscriber()
=> Dispose();
}
/// <summary><inheritdoc cref="EventSubscriber"/> </summary>
public sealed class EventSubscriber<T1> : IDisposable
{
private readonly string _label;
private readonly IPluginLog _log;
private readonly Dictionary<Action<T1>, Action<T1>> _delegates = new();
private ICallGateSubscriber<T1, object?>? _subscriber;
private bool _disabled;
public EventSubscriber(IDalamudPluginInterface pi, string label, params Action<T1>[] actions)
{
_label = label;
_log = PluginLogHelper.GetLog(pi);
try
{
_subscriber = pi.GetIpcSubscriber<T1, object?>(label);
foreach (var action in actions)
Event += action;
_disabled = false;
}
catch (Exception e)
{
_log.Error($"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <summary><inheritdoc cref="EventSubscriber.Enable"/> </summary>
public void Enable()
{
if (_disabled && _subscriber != null)
{
foreach (var action in _delegates.Values)
_subscriber.Subscribe(action);
_disabled = false;
}
}
/// <summary><inheritdoc cref="EventSubscriber.Disable"/> </summary>
public void Disable()
{
if (!_disabled)
{
if (_subscriber != null)
foreach (var action in _delegates.Values)
_subscriber.Unsubscribe(action);
_disabled = true;
}
}
/// <summary><inheritdoc cref="EventSubscriber.Event"/> </summary>
public event Action<T1> Event
{
add
{
if (_subscriber != null && !_delegates.ContainsKey(value))
{
void Action(T1 a)
{
try
{
value(a);
}
catch (Exception e)
{
_log.Error($"Exception invoking IPC event {_label}:\n{e}");
}
}
if (_delegates.TryAdd(value, Action) && !_disabled)
_subscriber.Subscribe(Action);
}
}
remove
{
if (_subscriber != null && _delegates.Remove(value, out var action))
_subscriber.Unsubscribe(action);
}
}
public void Dispose()
{
Disable();
_subscriber = null;
_delegates.Clear();
}
~EventSubscriber()
=> Dispose();
}
/// <summary><inheritdoc cref="EventSubscriber"/> </summary>
public sealed class EventSubscriber<T1, T2> : IDisposable
{
private readonly string _label;
private readonly IPluginLog _log;
private readonly Dictionary<Action<T1, T2>, Action<T1, T2>> _delegates = new();
private ICallGateSubscriber<T1, T2, object?>? _subscriber;
private bool _disabled;
public EventSubscriber(IDalamudPluginInterface pi, string label, params Action<T1, T2>[] actions)
{
_label = label;
_log = PluginLogHelper.GetLog(pi);
try
{
_subscriber = pi.GetIpcSubscriber<T1, T2, object?>(label);
foreach (var action in actions)
Event += action;
_disabled = false;
}
catch (Exception e)
{
_log.Error($"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <summary><inheritdoc cref="EventSubscriber.Enable"/> </summary>
public void Enable()
{
if (_disabled && _subscriber != null)
{
foreach (var action in _delegates.Values)
_subscriber.Subscribe(action);
_disabled = false;
}
}
/// <summary><inheritdoc cref="EventSubscriber.Disable"/> </summary>
public void Disable()
{
if (!_disabled)
{
if (_subscriber != null)
foreach (var action in _delegates.Values)
_subscriber.Unsubscribe(action);
_disabled = true;
}
}
/// <summary><inheritdoc cref="EventSubscriber.Event"/> </summary>
public event Action<T1, T2> Event
{
add
{
if (_subscriber != null && !_delegates.ContainsKey(value))
{
void Action(T1 a, T2 b)
{
try
{
value(a, b);
}
catch (Exception e)
{
_log.Error($"Exception invoking IPC event {_label}:\n{e}");
}
}
if (_delegates.TryAdd(value, Action) && !_disabled)
_subscriber.Subscribe(Action);
}
}
remove
{
if (_subscriber != null && _delegates.Remove(value, out var action))
_subscriber.Unsubscribe(action);
}
}
public void Dispose()
{
Disable();
_subscriber = null;
_delegates.Clear();
}
~EventSubscriber()
=> Dispose();
}
/// <summary><inheritdoc cref="EventSubscriber"/> </summary>
public sealed class EventSubscriber<T1, T2, T3> : IDisposable
{
private readonly string _label;
private readonly IPluginLog _log;
private readonly Dictionary<Action<T1, T2, T3>, Action<T1, T2, T3>> _delegates = new();
private ICallGateSubscriber<T1, T2, T3, object?>? _subscriber;
private bool _disabled;
public EventSubscriber(IDalamudPluginInterface pi, string label, params Action<T1, T2, T3>[] actions)
{
_label = label;
_log = PluginLogHelper.GetLog(pi);
try
{
_subscriber = pi.GetIpcSubscriber<T1, T2, T3, object?>(label);
foreach (var action in actions)
Event += action;
_disabled = false;
}
catch (Exception e)
{
_log.Error($"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <summary><inheritdoc cref="EventSubscriber.Enable"/> </summary>
public void Enable()
{
if (_disabled && _subscriber != null)
{
foreach (var action in _delegates.Values)
_subscriber.Subscribe(action);
_disabled = false;
}
}
/// <summary><inheritdoc cref="EventSubscriber.Disable"/> </summary>
public void Disable()
{
if (!_disabled)
{
if (_subscriber != null)
foreach (var action in _delegates.Values)
_subscriber.Unsubscribe(action);
_disabled = true;
}
}
/// <summary><inheritdoc cref="EventSubscriber.Event"/> </summary>
public event Action<T1, T2, T3> Event
{
add
{
if (_subscriber != null && !_delegates.ContainsKey(value))
{
void Action(T1 a, T2 b, T3 c)
{
try
{
value(a, b, c);
}
catch (Exception e)
{
_log.Error($"Exception invoking IPC event {_label}:\n{e}");
}
}
if (_delegates.TryAdd(value, Action) && !_disabled)
_subscriber.Subscribe(Action);
}
}
remove
{
if (_subscriber != null && _delegates.Remove(value, out var action))
_subscriber.Unsubscribe(action);
}
}
public void Dispose()
{
Disable();
_subscriber = null;
_delegates.Clear();
}
~EventSubscriber()
=> Dispose();
}

View File

@@ -0,0 +1,224 @@
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
namespace Glamourer.Api.Helpers;
/// <summary>
/// Specialized disposable Provider for Funcs.
/// </summary>
public sealed class FuncProvider<TRet> : IDisposable
{
private ICallGateProvider<TRet>? _provider;
public FuncProvider(IDalamudPluginInterface pi, string label, Func<TRet> func)
{
try
{
_provider = pi.GetIpcProvider<TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Provider for {label}\n{e}");
_provider = null;
}
_provider?.RegisterFunc(func);
}
public void Dispose()
{
_provider?.UnregisterFunc();
_provider = null;
GC.SuppressFinalize(this);
}
~FuncProvider()
=> Dispose();
}
/// <inheritdoc cref="FuncProvider{TRet}"/>
public sealed class FuncProvider<T1, TRet> : IDisposable
{
private ICallGateProvider<T1, TRet>? _provider;
public FuncProvider(IDalamudPluginInterface pi, string label, Func<T1, TRet> func)
{
try
{
_provider = pi.GetIpcProvider<T1, TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Provider for {label}\n{e}");
_provider = null;
}
_provider?.RegisterFunc(func);
}
public void Dispose()
{
_provider?.UnregisterFunc();
_provider = null;
GC.SuppressFinalize(this);
}
~FuncProvider()
=> Dispose();
}
/// <inheritdoc cref="FuncProvider{TRet}"/>
public sealed class FuncProvider<T1, T2, TRet> : IDisposable
{
private ICallGateProvider<T1, T2, TRet>? _provider;
public FuncProvider(IDalamudPluginInterface pi, string label, Func<T1, T2, TRet> func)
{
try
{
_provider = pi.GetIpcProvider<T1, T2, TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Provider for {label}\n{e}");
_provider = null;
}
_provider?.RegisterFunc(func);
}
public void Dispose()
{
_provider?.UnregisterFunc();
_provider = null;
GC.SuppressFinalize(this);
}
~FuncProvider()
=> Dispose();
}
/// <inheritdoc cref="FuncProvider{TRet}"/>
public sealed class FuncProvider<T1, T2, T3, TRet> : IDisposable
{
private ICallGateProvider<T1, T2, T3, TRet>? _provider;
public FuncProvider(IDalamudPluginInterface pi, string label, Func<T1, T2, T3, TRet> func)
{
try
{
_provider = pi.GetIpcProvider<T1, T2, T3, TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Provider for {label}\n{e}");
_provider = null;
}
_provider?.RegisterFunc(func);
}
public void Dispose()
{
_provider?.UnregisterFunc();
_provider = null;
GC.SuppressFinalize(this);
}
~FuncProvider()
=> Dispose();
}
/// <inheritdoc cref="FuncProvider{TRet}"/>
public sealed class FuncProvider<T1, T2, T3, T4, TRet> : IDisposable
{
private ICallGateProvider<T1, T2, T3, T4, TRet>? _provider;
public FuncProvider(IDalamudPluginInterface pi, string label, Func<T1, T2, T3, T4, TRet> func)
{
try
{
_provider = pi.GetIpcProvider<T1, T2, T3, T4, TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Provider for {label}\n{e}");
_provider = null;
}
_provider?.RegisterFunc(func);
}
public void Dispose()
{
_provider?.UnregisterFunc();
_provider = null;
GC.SuppressFinalize(this);
}
~FuncProvider()
=> Dispose();
}
/// <inheritdoc cref="FuncProvider{TRet}"/>
public sealed class FuncProvider<T1, T2, T3, T4, T5, TRet> : IDisposable
{
private ICallGateProvider<T1, T2, T3, T4, T5, TRet>? _provider;
public FuncProvider(IDalamudPluginInterface pi, string label, Func<T1, T2, T3, T4, T5, TRet> func)
{
try
{
_provider = pi.GetIpcProvider<T1, T2, T3, T4, T5, TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Provider for {label}\n{e}");
_provider = null;
}
_provider?.RegisterFunc(func);
}
public void Dispose()
{
_provider?.UnregisterFunc();
_provider = null;
GC.SuppressFinalize(this);
}
~FuncProvider()
=> Dispose();
}
/// <inheritdoc cref="FuncProvider{TRet}"/>
public sealed class FuncProvider<T1, T2, T3, T4, T5, T6, TRet> : IDisposable
{
private ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet>? _provider;
public FuncProvider(IDalamudPluginInterface pi, string label, Func<T1, T2, T3, T4, T5, T6, TRet> func)
{
try
{
_provider = pi.GetIpcProvider<T1, T2, T3, T4, T5, T6, TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Provider for {label}\n{e}");
_provider = null;
}
_provider?.RegisterFunc(func);
}
public void Dispose()
{
_provider?.UnregisterFunc();
_provider = null;
GC.SuppressFinalize(this);
}
~FuncProvider()
=> Dispose();
}

View File

@@ -0,0 +1,217 @@
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Ipc.Exceptions;
namespace Glamourer.Api.Helpers;
/// <summary>
/// Specialized subscriber only allowing to invoke functions with a return.
/// </summary>
public class FuncSubscriber<TRet>
{
private readonly string _label;
private readonly ICallGateSubscriber<TRet>? _subscriber;
/// <summary> Whether the subscriber could successfully be created. </summary>
public bool Valid
=> _subscriber != null;
/// <inheritdoc cref="FuncSubscriber{TRet}"/>
protected FuncSubscriber(IDalamudPluginInterface pi, string label)
{
_label = label;
try
{
_subscriber = pi.GetIpcSubscriber<TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <summary> Invoke the function. See the source of the subscriber for details.</summary>
protected TRet Invoke()
=> _subscriber != null ? _subscriber.InvokeFunc() : throw new IpcNotReadyError(_label);
}
/// <inheritdoc cref="FuncSubscriber{TRet}"/>
public class FuncSubscriber<T1, TRet>
{
private readonly string _label;
private readonly ICallGateSubscriber<T1, TRet>? _subscriber;
/// <inheritdoc cref="FuncSubscriber{TRet}.Valid"/>
public bool Valid
=> _subscriber != null;
/// <inheritdoc cref="FuncSubscriber{TRet}"/>
protected FuncSubscriber(IDalamudPluginInterface pi, string label)
{
_label = label;
try
{
_subscriber = pi.GetIpcSubscriber<T1, TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <inheritdoc cref="FuncSubscriber{TRet}.Invoke"/>
protected TRet Invoke(T1 a)
=> _subscriber != null ? _subscriber.InvokeFunc(a) : throw new IpcNotReadyError(_label);
}
/// <inheritdoc cref="FuncSubscriber{TRet}"/>
public class FuncSubscriber<T1, T2, TRet>
{
private readonly string _label;
private readonly ICallGateSubscriber<T1, T2, TRet>? _subscriber;
/// <inheritdoc cref="FuncSubscriber{TRet}.Valid"/>
public bool Valid
=> _subscriber != null;
/// <inheritdoc cref="FuncSubscriber{TRet}"/>
protected FuncSubscriber(IDalamudPluginInterface pi, string label)
{
_label = label;
try
{
_subscriber = pi.GetIpcSubscriber<T1, T2, TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <inheritdoc cref="FuncSubscriber{TRet}.Invoke"/>
protected TRet Invoke(T1 a, T2 b)
=> _subscriber != null ? _subscriber.InvokeFunc(a, b) : throw new IpcNotReadyError(_label);
}
/// <inheritdoc cref="FuncSubscriber{TRet}"/>
public class FuncSubscriber<T1, T2, T3, TRet>
{
private readonly string _label;
private readonly ICallGateSubscriber<T1, T2, T3, TRet>? _subscriber;
/// <inheritdoc cref="FuncSubscriber{TRet}.Valid"/>
public bool Valid
=> _subscriber != null;
/// <inheritdoc cref="FuncSubscriber{TRet}"/>
protected FuncSubscriber(IDalamudPluginInterface pi, string label)
{
_label = label;
try
{
_subscriber = pi.GetIpcSubscriber<T1, T2, T3, TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <inheritdoc cref="FuncSubscriber{TRet}.Invoke"/>
protected TRet Invoke(T1 a, T2 b, T3 c)
=> _subscriber != null ? _subscriber.InvokeFunc(a, b, c) : throw new IpcNotReadyError(_label);
}
/// <inheritdoc cref="FuncSubscriber{TRet}"/>
public class FuncSubscriber<T1, T2, T3, T4, TRet>
{
private readonly string _label;
private readonly ICallGateSubscriber<T1, T2, T3, T4, TRet>? _subscriber;
/// <inheritdoc cref="FuncSubscriber{TRet}.Valid"/>
public bool Valid
=> _subscriber != null;
/// <inheritdoc cref="FuncSubscriber{TRet}"/>
protected FuncSubscriber(IDalamudPluginInterface pi, string label)
{
_label = label;
try
{
_subscriber = pi.GetIpcSubscriber<T1, T2, T3, T4, TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <inheritdoc cref="FuncSubscriber{TRet}.Invoke"/>
protected TRet Invoke(T1 a, T2 b, T3 c, T4 d)
=> _subscriber != null ? _subscriber.InvokeFunc(a, b, c, d) : throw new IpcNotReadyError(_label);
}
/// <inheritdoc cref="FuncSubscriber{TRet}"/>
public class FuncSubscriber<T1, T2, T3, T4, T5, TRet>
{
private readonly string _label;
private readonly ICallGateSubscriber<T1, T2, T3, T4, T5, TRet>? _subscriber;
/// <inheritdoc cref="FuncSubscriber{TRet}.Valid"/>
public bool Valid
=> _subscriber != null;
/// <inheritdoc cref="FuncSubscriber{TRet}"/>
protected FuncSubscriber(IDalamudPluginInterface pi, string label)
{
_label = label;
try
{
_subscriber = pi.GetIpcSubscriber<T1, T2, T3, T4, T5, TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <inheritdoc cref="FuncSubscriber{TRet}.Invoke"/>
protected TRet Invoke(T1 a, T2 b, T3 c, T4 d, T5 e)
=> _subscriber != null ? _subscriber.InvokeFunc(a, b, c, d, e) : throw new IpcNotReadyError(_label);
}
/// <inheritdoc cref="FuncSubscriber{TRet}"/>
public class FuncSubscriber<T1, T2, T3, T4, T5, T6, TRet>
{
private readonly string _label;
private readonly ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet>? _subscriber;
/// <inheritdoc cref="FuncSubscriber{TRet}.Valid"/>
public bool Valid
=> _subscriber != null;
/// <inheritdoc cref="FuncSubscriber{TRet}"/>
protected FuncSubscriber(IDalamudPluginInterface pi, string label)
{
_label = label;
try
{
_subscriber = pi.GetIpcSubscriber<T1, T2, T3, T4, T5, T6, TRet>(label);
}
catch (Exception e)
{
PluginLogHelper.WriteError(pi, $"Error registering IPC Subscriber for {label}\n{e}");
_subscriber = null;
}
}
/// <inheritdoc cref="FuncSubscriber{TRet}.Invoke"/>
protected TRet Invoke(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f)
=> _subscriber != null ? _subscriber.InvokeFunc(a, b, c, d, e, f) : throw new IpcNotReadyError(_label);
}

View File

@@ -0,0 +1,26 @@
using Dalamud.IoC;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
namespace Glamourer.Api.Helpers;
internal class PluginLogHelper
{
[PluginService]
private static IPluginLog? _log { get; set; }
private PluginLogHelper(IDalamudPluginInterface pi)
=> pi.Inject(this);
public static void WriteError(IDalamudPluginInterface pi, string errorMessage)
=> GetLog(pi).Error(errorMessage);
public static IPluginLog GetLog(IDalamudPluginInterface pi)
{
if (_log != null)
return _log;
_ = new PluginLogHelper(pi);
return _log!;
}
}

View File

@@ -0,0 +1,52 @@
using Dalamud.Plugin;
using Glamourer.Api.Api;
using Glamourer.Api.Enums;
using Glamourer.Api.Helpers;
namespace Glamourer.Api.IpcSubscribers;
/// <inheritdoc cref="IGlamourerApiDesigns.GetDesignList"/>
public sealed class GetDesignList(IDalamudPluginInterface pi)
: FuncSubscriber<Dictionary<Guid, string>>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(GetDesignList)}.V2";
/// <inheritdoc cref="IGlamourerApiDesigns.GetDesignList"/>
public new Dictionary<Guid, string> Invoke()
=> base.Invoke();
/// <summary> Create a provider. </summary>
public static FuncProvider<Dictionary<Guid, string>> Provider(IDalamudPluginInterface pi, IGlamourerApiDesigns api)
=> new(pi, Label, api.GetDesignList);
}
/// <inheritdoc cref="IGlamourerApiDesigns.ApplyDesign"/>
public sealed class ApplyDesign(IDalamudPluginInterface pi) : FuncSubscriber<Guid, int, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(ApplyDesign)}";
/// <inheritdoc cref="IGlamourerApiDesigns.ApplyDesign"/>
public GlamourerApiEc Invoke(Guid designId, int objectIndex, uint key = 0, ApplyFlag flags = ApplyFlagEx.DesignDefault)
=> (GlamourerApiEc)Invoke(designId, objectIndex, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<Guid, int, uint, ulong, int> Provider(IDalamudPluginInterface pi, IGlamourerApiDesigns api)
=> new(pi, Label, (a, b, c, d) => (int)api.ApplyDesign(a, b, c, (ApplyFlag)d));
}
/// <inheritdoc cref="IGlamourerApiDesigns.ApplyDesignName"/>
public sealed class ApplyDesignName(IDalamudPluginInterface pi) : FuncSubscriber<Guid, string, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(ApplyDesignName)}";
/// <inheritdoc cref="IGlamourerApiDesigns.ApplyDesignName"/>
public GlamourerApiEc Invoke(Guid designId, string objectName, uint key = 0, ApplyFlag flags = ApplyFlagEx.DesignDefault)
=> (GlamourerApiEc)Invoke(designId, objectName, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<Guid, string, uint, ulong, int> Provider(IDalamudPluginInterface pi, IGlamourerApiDesigns api)
=> new(pi, Label, (a, b, c, d) => (int)api.ApplyDesignName(a, b, c, (ApplyFlag)d));
}

View File

@@ -0,0 +1,110 @@
using Dalamud.Plugin;
using Glamourer.Api.Api;
using Glamourer.Api.Enums;
using Glamourer.Api.Helpers;
namespace Glamourer.Api.IpcSubscribers;
/// <inheritdoc cref="IGlamourerApiItems.SetItem"/>
public sealed class SetItem(IDalamudPluginInterface pi)
: FuncSubscriber<int, byte, ulong, IReadOnlyList<byte>, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(SetItem)}.V3";
/// <inheritdoc cref="IGlamourerApiItems.SetItem"/>
public GlamourerApiEc Invoke(int objectIndex, ApiEquipSlot slot, ulong itemId, IReadOnlyList<byte> stain, uint key = 0,
ApplyFlag flags = ApplyFlag.Once)
=> (GlamourerApiEc)Invoke(objectIndex, (byte)slot, itemId, stain, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<int, byte, ulong, IReadOnlyList<byte>, uint, ulong, int> Provider(IDalamudPluginInterface pi,
IGlamourerApiItems api)
=> new(pi, Label, (a, b, c, d, e, f) => (int)api.SetItem(a, (ApiEquipSlot)b, c, d, e, (ApplyFlag)f));
}
/// <inheritdoc cref="IGlamourerApiItems.SetItemName"/>
public sealed class SetItemName(IDalamudPluginInterface pi)
: FuncSubscriber<string, byte, ulong, IReadOnlyList<byte>, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(SetItemName)}.V2";
/// <inheritdoc cref="IGlamourerApiItems.SetItem"/>
public GlamourerApiEc Invoke(string objectName, ApiEquipSlot slot, ulong itemId, IReadOnlyList<byte> stain, uint key = 0,
ApplyFlag flags = ApplyFlag.Once)
=> (GlamourerApiEc)Invoke(objectName, (byte)slot, itemId, stain, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<string, byte, ulong, IReadOnlyList<byte>, uint, ulong, int> Provider(IDalamudPluginInterface pi,
IGlamourerApiItems api)
=> new(pi, Label, (a, b, c, d, e, f) => (int)api.SetItemName(a, (ApiEquipSlot)b, c, d, e, (ApplyFlag)f));
}
/// <inheritdoc cref="IGlamourerApiItems.SetBonusItem"/>
public sealed class SetBonusItem(IDalamudPluginInterface pi)
: FuncSubscriber<int, byte, ulong, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(SetBonusItem)}";
/// <inheritdoc cref="IGlamourerApiItems.SetBonusItem"/>
public GlamourerApiEc Invoke(int objectIndex, ApiBonusSlot slot, ulong itemId, uint key = 0, ApplyFlag flags = ApplyFlag.Once)
=> (GlamourerApiEc)Invoke(objectIndex, (byte)slot, itemId, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<int, byte, ulong, uint, ulong, int> Provider(IDalamudPluginInterface pi, IGlamourerApiItems api)
=> new(pi, Label, (a, b, c, d, e) => (int)api.SetBonusItem(a, (ApiBonusSlot)b, c, d, (ApplyFlag)e));
}
/// <inheritdoc cref="IGlamourerApiItems.SetBonusItemName"/>
public sealed class SetBonusItemName(IDalamudPluginInterface pi)
: FuncSubscriber<string, byte, ulong, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(SetBonusItemName)}.V2";
/// <inheritdoc cref="IGlamourerApiItems.SetBonusItemName"/>
public GlamourerApiEc Invoke(string objectName, ApiBonusSlot slot, ulong itemId, uint key = 0, ApplyFlag flags = ApplyFlag.Once)
=> (GlamourerApiEc)Invoke(objectName, (byte)slot, itemId, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<string, byte, ulong, uint, ulong, int> Provider(IDalamudPluginInterface pi, IGlamourerApiItems api)
=> new(pi, Label, (a, b, c, d, e) => (int)api.SetBonusItemName(a, (ApiBonusSlot)b, c, d, (ApplyFlag)e));
}
/// <inheritdoc cref="IGlamourerApiItems.SetMetaState"/>
public sealed class SetMetaState(IDalamudPluginInterface pi)
: FuncSubscriber<int, ulong, bool, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(SetMetaState)}";
/// <inheritdoc cref="IGlamourerApiItems.SetMetaState"/>
public GlamourerApiEc Invoke(int objectIndex, MetaFlag types, bool newValue, uint key = 0,
ApplyFlag flags = ApplyFlag.Once)
=> (GlamourerApiEc)Invoke(objectIndex, (ulong)types, newValue, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<int, ulong, bool, uint, ulong, int> Provider(IDalamudPluginInterface pi,
IGlamourerApiItems api)
=> new(pi, Label, (a, b, c, d, e) => (int)api.SetMetaState(a, (MetaFlag)b, c, d, (ApplyFlag)e));
}
/// <inheritdoc cref="IGlamourerApiItems.SetMetaStateName"/>
public sealed class SetMetaStateName(IDalamudPluginInterface pi)
: FuncSubscriber<string, ulong, bool, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(SetMetaStateName)}";
/// <inheritdoc cref="IGlamourerApiItems.SetMetaStateName"/>
public GlamourerApiEc Invoke(string objectName, MetaFlag types, bool newValue, uint key = 0,
ApplyFlag flags = ApplyFlag.Once)
=> (GlamourerApiEc)Invoke(objectName, (ulong)types, newValue, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<string, ulong, bool, uint, ulong, int> Provider(IDalamudPluginInterface pi,
IGlamourerApiItems api)
=> new(pi, Label, (a, b, c, d, e) => (int)api.SetMetaStateName(a, (MetaFlag)b, c, d, (ApplyFlag)e));
}

View File

@@ -0,0 +1,52 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin;
using Glamourer.Api.Helpers;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Glamourer.Api.IpcSubscribers.Legacy;
public sealed class GetDesignList(IDalamudPluginInterface pi)
: FuncSubscriber<(string Name, Guid Identifier)[]>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(GetDesignList)}";
public new (string Name, Guid Identifier)[] Invoke()
=> base.Invoke();
}
public sealed class ApplyByGuid(IDalamudPluginInterface pi)
: ActionSubscriber<Guid, string>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyByGuid)}";
public new void Invoke(Guid design, string name)
=> base.Invoke(design, name);
}
public sealed class ApplyByGuidOnce(IDalamudPluginInterface pi)
: ActionSubscriber<Guid, string>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyByGuidOnce)}";
public new void Invoke(Guid design, string name)
=> base.Invoke(design, name);
}
public sealed class ApplyByGuidToCharacter(IDalamudPluginInterface pi)
: ActionSubscriber<Guid, ICharacter?>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyByGuidToCharacter)}";
public new void Invoke(Guid design, ICharacter? character)
=> base.Invoke(design, character);
}
public sealed class ApplyByGuidOnceToCharacter(IDalamudPluginInterface pi)
: ActionSubscriber<Guid, ICharacter?>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyByGuidOnceToCharacter)}";
public new void Invoke(Guid design, ICharacter? character)
=> base.Invoke(design, character);
}

View File

@@ -0,0 +1,66 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin;
using Glamourer.Api.Api;
using Glamourer.Api.Enums;
using Glamourer.Api.Helpers;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Glamourer.Api.IpcSubscribers.Legacy;
public sealed class SetItem(IDalamudPluginInterface pi)
: FuncSubscriber<ICharacter?, byte, ulong, byte, uint, int>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(SetItem)}";
public new GlamourerApiEc Invoke(ICharacter? character, byte slot, ulong itemId, byte stainId, uint key)
=> (GlamourerApiEc)base.Invoke(character, slot, itemId, stainId, key);
}
public sealed class SetItemOnce(IDalamudPluginInterface pi)
: FuncSubscriber<ICharacter?, byte, ulong, byte, uint, int>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(SetItemOnce)}";
public new GlamourerApiEc Invoke(ICharacter? character, byte slot, ulong itemId, byte stainId, uint key)
=> (GlamourerApiEc)base.Invoke(character, slot, itemId, stainId, key);
}
public sealed class SetItemByActorName(IDalamudPluginInterface pi)
: FuncSubscriber<string, byte, ulong, byte, uint, int>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(SetItemByActorName)}";
public new GlamourerApiEc Invoke(string actorName, byte slot, ulong itemId, byte stainId, uint key)
=> (GlamourerApiEc)base.Invoke(actorName, slot, itemId, stainId, key);
}
public sealed class SetItemOnceByActorName(IDalamudPluginInterface pi)
: FuncSubscriber<string, byte, ulong, byte, uint, int>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(SetItemOnceByActorName)}";
public new GlamourerApiEc Invoke(string actorName, byte slot, ulong itemId, byte stainId, uint key)
=> (GlamourerApiEc)base.Invoke(actorName, slot, itemId, stainId, key);
}
public sealed class SetItemV2(IDalamudPluginInterface pi)
: FuncSubscriber<int, byte, ulong, byte, uint, ulong, int>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(SetItem)}.V2";
public GlamourerApiEc Invoke(int objectIndex, ApiEquipSlot slot, ulong itemId, byte stain, uint key = 0, ApplyFlag flags = ApplyFlag.Once)
=> (GlamourerApiEc)Invoke(objectIndex, (byte)slot, itemId, stain, key, (ulong)flags);
}
public sealed class SetItemName(IDalamudPluginInterface pi)
: FuncSubscriber<string, byte, ulong, byte, uint, ulong, int>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(SetItemName)}";
public GlamourerApiEc Invoke(string objectName, ApiEquipSlot slot, ulong itemId, byte stain, uint key = 0, ApplyFlag flags = ApplyFlag.Once)
=> (GlamourerApiEc)Invoke(objectName, (byte)slot, itemId, stain, key, (ulong)flags);
public static FuncProvider<string, byte, ulong, byte, uint, ulong, int> Provider(IDalamudPluginInterface pi, IGlamourerApiItems api)
=> new(pi, Label, (a, b, c, d, e, f) => (int)api.SetItemName(a, (ApiEquipSlot)b, c, [d], e, (ApplyFlag)f));
}

View File

@@ -0,0 +1,15 @@
using Dalamud.Plugin;
using Glamourer.Api.Helpers;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Glamourer.Api.IpcSubscribers.Legacy;
public sealed class ApiVersions(IDalamudPluginInterface pi)
: FuncSubscriber<(int, int)>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApiVersions)}";
public new (int Major, int Minor) Invoke()
=> base.Invoke();
}

View File

@@ -0,0 +1,250 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin;
using Glamourer.Api.Helpers;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Glamourer.Api.IpcSubscribers.Legacy;
public sealed class Revert(IDalamudPluginInterface pi)
: ActionSubscriber<string>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(Revert)}";
public new void Invoke(string characterName)
=> base.Invoke(characterName);
}
public sealed class RevertCharacter(IDalamudPluginInterface pi)
: ActionSubscriber<ICharacter?>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(RevertCharacter)}";
public new void Invoke(ICharacter? character)
=> base.Invoke(character);
}
public sealed class RevertLock(IDalamudPluginInterface pi)
: ActionSubscriber<string, uint>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(RevertLock)}";
public new void Invoke(string characterName, uint key)
=> base.Invoke(characterName, key);
}
public sealed class RevertCharacterLock(IDalamudPluginInterface pi)
: ActionSubscriber<ICharacter?, uint>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(RevertCharacterLock)}";
public new void Invoke(ICharacter? character, uint key)
=> base.Invoke(character, key);
}
public sealed class RevertToAutomation(IDalamudPluginInterface pi)
: FuncSubscriber<string, uint, bool>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(RevertToAutomation)}";
public new bool Invoke(string characterName, uint key)
=> base.Invoke(characterName, key);
}
public sealed class RevertToAutomationCharacter(IDalamudPluginInterface pi)
: FuncSubscriber<ICharacter?, uint, bool>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(RevertToAutomationCharacter)}";
public new bool Invoke(ICharacter? character, uint key)
=> base.Invoke(character, key);
}
public sealed class Unlock(IDalamudPluginInterface pi)
: FuncSubscriber<ICharacter?, uint, bool>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(Unlock)}";
public new bool Invoke(ICharacter? character, uint key)
=> base.Invoke(character, key);
}
public sealed class UnlockName(IDalamudPluginInterface pi)
: FuncSubscriber<string, uint, bool>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(UnlockName)}";
public new bool Invoke(string characterName, uint key)
=> base.Invoke(characterName, key);
}
public static class StateChanged
{
public const string Label = $"Penumbra.{nameof(StateChanged)}";
public static EventSubscriber<int, nint, Lazy<string>> Subscriber(IDalamudPluginInterface pi,
params Action<int, nint, Lazy<string>>[] actions)
=> new(pi, Label, actions);
}
public sealed class GetAllCustomization(IDalamudPluginInterface pi)
: FuncSubscriber<string, string?>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(GetAllCustomization)}";
public new string? Invoke(string characterName)
=> base.Invoke(characterName);
}
public sealed class GetAllCustomizationFromCharacter(IDalamudPluginInterface pi)
: FuncSubscriber<ICharacter?, string?>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(GetAllCustomizationFromCharacter)}";
public new string? Invoke(ICharacter? character)
=> base.Invoke(character);
}
public sealed class GetAllCustomizationLocked(IDalamudPluginInterface pi)
: FuncSubscriber<string, uint, string?>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(GetAllCustomizationLocked)}";
public new string? Invoke(string characterName, uint key)
=> base.Invoke(characterName, key);
}
public sealed class GetAllCustomizationFromLockedCharacter(IDalamudPluginInterface pi)
: FuncSubscriber<ICharacter?, uint, string?>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(GetAllCustomizationFromLockedCharacter)}";
public new string? Invoke(ICharacter? character, uint key)
=> base.Invoke(character, key);
}
public sealed class ApplyAll(IDalamudPluginInterface pi)
: ActionSubscriber<string, string>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyAll)}";
public new void Invoke(string characterName, string stateBase64)
=> base.Invoke(characterName, stateBase64);
}
public sealed class ApplyAllOnce(IDalamudPluginInterface pi)
: ActionSubscriber<string, string>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyAllOnce)}";
public new void Invoke(string characterName, string stateBase64)
=> base.Invoke(characterName, stateBase64);
}
public sealed class ApplyAllToCharacter(IDalamudPluginInterface pi)
: ActionSubscriber<ICharacter?, string>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyAllToCharacter)}";
public new void Invoke(ICharacter? character, string stateBase64)
=> base.Invoke(character, stateBase64);
}
public sealed class ApplyAllOnceToCharacter(IDalamudPluginInterface pi)
: ActionSubscriber<ICharacter?, string>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyAllOnceToCharacter)}";
public new void Invoke(ICharacter? character, string stateBase64)
=> base.Invoke(character, stateBase64);
}
public sealed class ApplyOnlyEquipment(IDalamudPluginInterface pi)
: ActionSubscriber<string, string>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyOnlyEquipment)}";
public new void Invoke(string characterName, string stateBase64)
=> base.Invoke(characterName, stateBase64);
}
public sealed class ApplyOnlyEquipmentToCharacter(IDalamudPluginInterface pi)
: ActionSubscriber<ICharacter?, string>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyOnlyEquipmentToCharacter)}";
public new void Invoke(ICharacter? character, string stateBase64)
=> base.Invoke(character, stateBase64);
}
public sealed class ApplyOnlyCustomization(IDalamudPluginInterface pi)
: ActionSubscriber<string, string>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyOnlyCustomization)}";
public new void Invoke(string characterName, string stateBase64)
=> base.Invoke(characterName, stateBase64);
}
public sealed class ApplyOnlyCustomizationToCharacter(IDalamudPluginInterface pi)
: ActionSubscriber<ICharacter?, string>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyOnlyCustomizationToCharacter)}";
public new void Invoke(ICharacter? character, string stateBase64)
=> base.Invoke(character, stateBase64);
}
public sealed class ApplyAllLock(IDalamudPluginInterface pi)
: ActionSubscriber<string, string, uint>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyAllLock)}";
public new void Invoke(string characterName, string stateBase64, uint key)
=> base.Invoke(characterName, stateBase64, key);
}
public sealed class ApplyAllToCharacterLock(IDalamudPluginInterface pi)
: ActionSubscriber<ICharacter?, string, uint>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyAllToCharacterLock)}";
public new void Invoke(ICharacter? character, string stateBase64, uint key)
=> base.Invoke(character, stateBase64, key);
}
public sealed class ApplyOnlyEquipmentLock(IDalamudPluginInterface pi)
: ActionSubscriber<string, string, uint>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyOnlyEquipmentLock)}";
public new void Invoke(string characterName, string stateBase64, uint key)
=> base.Invoke(characterName, stateBase64, key);
}
public sealed class ApplyOnlyEquipmentToCharacterLock(IDalamudPluginInterface pi)
: ActionSubscriber<ICharacter?, string, uint>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyOnlyEquipmentToCharacterLock)}";
public new void Invoke(ICharacter? character, string stateBase64, uint key)
=> base.Invoke(character, stateBase64, key);
}
public sealed class ApplyOnlyCustomizationLock(IDalamudPluginInterface pi)
: ActionSubscriber<string, string, uint>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyOnlyCustomizationLock)}";
public new void Invoke(string characterName, string stateBase64, uint key)
=> base.Invoke(characterName, stateBase64, key);
}
public sealed class ApplyOnlyCustomizationToCharacterLock(IDalamudPluginInterface pi)
: ActionSubscriber<ICharacter?, string, uint>(pi, Label)
{
public const string Label = $"Glamourer.{nameof(ApplyOnlyCustomizationToCharacterLock)}";
public new void Invoke(ICharacter? character, string stateBase64, uint key)
=> base.Invoke(character, stateBase64, key);
}

View File

@@ -0,0 +1,51 @@
using Dalamud.Plugin;
using Glamourer.Api.Api;
using Glamourer.Api.Helpers;
namespace Glamourer.Api.IpcSubscribers;
/// <inheritdoc cref="IGlamourerApiBase.ApiVersion"/>
public sealed class ApiVersion(IDalamudPluginInterface pi)
: FuncSubscriber<(int, int)>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(ApiVersion)}.V2";
/// <inheritdoc cref="IGlamourerApiBase.ApiVersion"/>
public new (int Major, int Minor) Invoke()
=> base.Invoke();
/// <summary> Create a provider. </summary>
public static FuncProvider<(int, int)> Provider(IDalamudPluginInterface pi, IGlamourerApiBase api)
=> new(pi, Label, () => api.ApiVersion);
}
/// <summary> Triggered when the Glamourer API is initialized and ready. </summary>
public static class Initialized
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(Initialized)}";
/// <summary> Create a new event subscriber. </summary>
public static EventSubscriber Subscriber(IDalamudPluginInterface pi, params Action[] actions)
=> new(pi, Label, actions);
/// <summary> Create a provider. </summary>
public static EventProvider Provider(IDalamudPluginInterface pi)
=> new(pi, Label);
}
/// <summary> Triggered when the Glamourer API is fully disposed and unavailable. </summary>
public static class Disposed
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(Disposed)}";
/// <summary> Create a new event subscriber. </summary>
public static EventSubscriber Subscriber(IDalamudPluginInterface pi, params Action[] actions)
=> new(pi, Label, actions);
/// <summary> Create a provider. </summary>
public static EventProvider Provider(IDalamudPluginInterface pi)
=> new(pi, Label);
}

View File

@@ -0,0 +1,311 @@
using Dalamud.Plugin;
using Glamourer.Api.Api;
using Glamourer.Api.Enums;
using Glamourer.Api.Helpers;
using Newtonsoft.Json.Linq;
namespace Glamourer.Api.IpcSubscribers;
/// <inheritdoc cref="IGlamourerApiState.GetState"/>
public sealed class GetState(IDalamudPluginInterface pi)
: FuncSubscriber<int, uint, (int, JObject?)>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(GetState)}";
/// <inheritdoc cref="IGlamourerApiState.GetState"/>
public new (GlamourerApiEc, JObject?) Invoke(int objectIndex, uint key = 0)
{
var (ec, data) = base.Invoke(objectIndex, key);
return ((GlamourerApiEc)ec, data);
}
/// <summary> Create a provider. </summary>
public static FuncProvider<int, uint, (int, JObject?)> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (a, b) =>
{
var (ec, data) = api.GetState(a, b);
return ((int)ec, data);
});
}
/// <inheritdoc cref="IGlamourerApiState.GetStateName"/>
public sealed class GetStateName(IDalamudPluginInterface pi)
: FuncSubscriber<string, uint, (int, JObject?)>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(GetStateName)}";
/// <inheritdoc cref="IGlamourerApiState.GetStateName"/>
public new (GlamourerApiEc, JObject?) Invoke(string objectName, uint key = 0)
{
var (ec, data) = base.Invoke(objectName, key);
return ((GlamourerApiEc)ec, data);
}
/// <summary> Create a provider. </summary>
public static FuncProvider<string, uint, (int, JObject?)> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (i, k) =>
{
var (ec, data) = api.GetStateName(i, k);
return ((int)ec, data);
});
}
/// <inheritdoc cref="IGlamourerApiState.GetStateBase64"/>
public sealed class GetStateBase64(IDalamudPluginInterface pi)
: FuncSubscriber<int, uint, (int, string?)>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(GetStateBase64)}";
/// <inheritdoc cref="IGlamourerApiState.GetStateBase64"/>
public new (GlamourerApiEc, string?) Invoke(int objectIndex, uint key = 0)
{
var (ec, data) = base.Invoke(objectIndex, key);
return ((GlamourerApiEc)ec, data);
}
/// <summary> Create a provider. </summary>
public static FuncProvider<int, uint, (int, string?)> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (a, b) =>
{
var (ec, data) = api.GetStateBase64(a, b);
return ((int)ec, data);
});
}
/// <inheritdoc cref="IGlamourerApiState.GetStateBase64Name"/>
public sealed class GetStateBase64Name(IDalamudPluginInterface pi)
: FuncSubscriber<string, uint, (int, string?)>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(GetStateBase64Name)}";
/// <inheritdoc cref="IGlamourerApiState.GetStateBase64Name"/>
public new (GlamourerApiEc, string?) Invoke(string objectName, uint key = 0)
{
var (ec, data) = base.Invoke(objectName, key);
return ((GlamourerApiEc)ec, data);
}
/// <summary> Create a provider. </summary>
public static FuncProvider<string, uint, (int, string?)> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (i, k) =>
{
var (ec, data) = api.GetStateBase64Name(i, k);
return ((int)ec, data);
});
}
/// <inheritdoc cref="IGlamourerApiState.ApplyState"/>
public sealed class ApplyState(IDalamudPluginInterface pi)
: FuncSubscriber<object, int, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(ApplyState)}";
/// <inheritdoc cref="IGlamourerApiState.ApplyState"/>
public GlamourerApiEc Invoke(JObject state, int objectIndex, uint key = 0, ApplyFlag flags = ApplyFlagEx.StateDefault)
=> (GlamourerApiEc)Invoke(state, objectIndex, key, (ulong)flags);
/// <inheritdoc cref="IGlamourerApiState.ApplyState"/>
public GlamourerApiEc Invoke(string base64State, int objectIndex, uint key = 0, ApplyFlag flags = ApplyFlagEx.StateDefault)
=> (GlamourerApiEc)Invoke(base64State, objectIndex, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<object, int, uint, ulong, int> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (a, b, c, d) => (int)api.ApplyState(a, b, c, (ApplyFlag)d));
}
/// <inheritdoc cref="IGlamourerApiState.ApplyStateName"/>
public sealed class ApplyStateName(IDalamudPluginInterface pi)
: FuncSubscriber<object, string, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(ApplyStateName)}";
/// <inheritdoc cref="IGlamourerApiState.ApplyState"/>
public GlamourerApiEc Invoke(JObject state, string objectName, uint key = 0, ApplyFlag flags = ApplyFlagEx.StateDefault)
=> (GlamourerApiEc)Invoke(state, objectName, key, (ulong)flags);
/// <inheritdoc cref="IGlamourerApiState.ApplyState"/>
public GlamourerApiEc Invoke(string base64State, string objectName, uint key = 0, ApplyFlag flags = ApplyFlagEx.StateDefault)
=> (GlamourerApiEc)Invoke(base64State, objectName, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<object, string, uint, ulong, int> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (a, b, c, d) => (int)api.ApplyStateName(a, b, c, (ApplyFlag)d));
}
/// <inheritdoc cref="IGlamourerApiState.RevertState"/>
public sealed class RevertState(IDalamudPluginInterface pi)
: FuncSubscriber<int, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(RevertState)}";
/// <inheritdoc cref="IGlamourerApiState.RevertState"/>
public GlamourerApiEc Invoke(int objectIndex, uint key = 0, ApplyFlag flags = ApplyFlagEx.RevertDefault)
=> (GlamourerApiEc)Invoke(objectIndex, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<int, uint, ulong, int> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (a, b, c) => (int)api.RevertState(a, b, (ApplyFlag)c));
}
/// <inheritdoc cref="IGlamourerApiState.RevertStateName"/>
public sealed class RevertStateName(IDalamudPluginInterface pi)
: FuncSubscriber<string, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(RevertStateName)}";
/// <inheritdoc cref="IGlamourerApiState.RevertStateName"/>
public GlamourerApiEc Invoke(string objectName, uint key = 0, ApplyFlag flags = ApplyFlagEx.RevertDefault)
=> (GlamourerApiEc)Invoke(objectName, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<string, uint, ulong, int> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (a, b, c) => (int)api.RevertStateName(a, b, (ApplyFlag)c));
}
/// <inheritdoc cref="IGlamourerApiState.UnlockState"/>
public sealed class UnlockState(IDalamudPluginInterface pi)
: FuncSubscriber<int, uint, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(UnlockState)}";
/// <inheritdoc cref="IGlamourerApiState.UnlockState"/>
public new GlamourerApiEc Invoke(int objectIndex, uint key = 0)
=> (GlamourerApiEc)base.Invoke(objectIndex, key);
/// <summary> Create a provider. </summary>
public static FuncProvider<int, uint, int> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (a, b) => (int)api.UnlockState(a, b));
}
/// <inheritdoc cref="IGlamourerApiState.UnlockStateName"/>
public sealed class UnlockStateName(IDalamudPluginInterface pi)
: FuncSubscriber<string, uint, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(UnlockStateName)}";
/// <inheritdoc cref="IGlamourerApiState.UnlockStateName"/>
public new GlamourerApiEc Invoke(string objectName, uint key = 0)
=> (GlamourerApiEc)base.Invoke(objectName, key);
/// <summary> Create a provider. </summary>
public static FuncProvider<string, uint, int> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (a, b) => (int)api.UnlockStateName(a, b));
}
/// <inheritdoc cref="IGlamourerApiState.UnlockAll"/>
public sealed class UnlockAll(IDalamudPluginInterface pi)
: FuncSubscriber<uint, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(UnlockAll)}";
/// <inheritdoc cref="IGlamourerApiState.UnlockAll"/>
public new int Invoke(uint key)
=> base.Invoke(key);
/// <summary> Create a provider. </summary>
public static FuncProvider<uint, int> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, api.UnlockAll);
}
/// <inheritdoc cref="IGlamourerApiState.RevertToAutomation"/>
public sealed class RevertToAutomation(IDalamudPluginInterface pi)
: FuncSubscriber<int, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(RevertToAutomation)}.V2";
/// <inheritdoc cref="IGlamourerApiState.RevertToAutomation"/>
public GlamourerApiEc Invoke(int objectIndex, uint key = 0, ApplyFlag flags = ApplyFlagEx.RevertDefault)
=> (GlamourerApiEc)Invoke(objectIndex, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<int, uint, ulong, int> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (a, b, c) => (int)api.RevertToAutomation(a, b, (ApplyFlag)c));
}
/// <inheritdoc cref="IGlamourerApiState.RevertToAutomationName"/>
public sealed class RevertToAutomationName(IDalamudPluginInterface pi)
: FuncSubscriber<string, uint, ulong, int>(pi, Label)
{
/// <summary> The label. </summary>
public const string Label = $"Glamourer.{nameof(RevertToAutomationName)}";
/// <inheritdoc cref="IGlamourerApiState.RevertToAutomationName"/>
public GlamourerApiEc Invoke(string objectName, uint key = 0, ApplyFlag flags = ApplyFlagEx.RevertDefault)
=> (GlamourerApiEc)Invoke(objectName, key, (ulong)flags);
/// <summary> Create a provider. </summary>
public static FuncProvider<string, uint, ulong, int> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (a, b, c) => (int)api.RevertToAutomationName(a, b, (ApplyFlag)c));
}
/// <inheritdoc cref="IGlamourerApiState.StateChanged" />
public static class StateChanged
{
/// <summary> The label. </summary>
public const string Label = $"Penumbra.{nameof(StateChanged)}.V2";
/// <summary> Create a new event subscriber. </summary>
public static EventSubscriber<nint> Subscriber(IDalamudPluginInterface pi, params Action<nint>[] actions)
=> new(pi, Label, actions);
/// <summary> Create a provider. </summary>
public static EventProvider<nint> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (t => api.StateChanged += t, t => api.StateChanged -= t));
}
/// <inheritdoc cref="IGlamourerApiState.StateChangedWithType" />
public static class StateChangedWithType
{
/// <summary> The label. </summary>
public const string Label = $"Penumbra.{nameof(StateChangedWithType)}";
/// <summary> Create a new event subscriber. </summary>
public static EventSubscriber<nint, StateChangeType> Subscriber(IDalamudPluginInterface pi, params Action<nint, StateChangeType>[] actions)
=> new(pi, Label, actions);
/// <summary> Create a provider. </summary>
public static EventProvider<nint, StateChangeType> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (t => api.StateChangedWithType += t, t => api.StateChangedWithType -= t));
}
/// <inheritdoc cref="IGlamourerApiState.StateFinalized" />
public static class StateFinalized
{
/// <summary> The label. </summary>
public const string Label = $"Penumbra.{nameof(StateFinalized)}";
/// <summary> Create a new event subscriber. </summary>
public static EventSubscriber<nint, StateFinalizationType> Subscriber(IDalamudPluginInterface pi, params Action<nint, StateFinalizationType>[] actions)
=> new(pi, Label, actions);
/// <summary> Create a provider. </summary>
public static EventProvider<nint, StateFinalizationType> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (t => api.StateFinalized += t, t => api.StateFinalized -= t));
}
/// <inheritdoc cref="IGlamourerApiState.GPoseChanged" />
public static class GPoseChanged
{
/// <summary> The label. </summary>
public const string Label = $"Penumbra.{nameof(GPoseChanged)}";
/// <summary> Create a new event subscriber. </summary>
public static EventSubscriber<bool> Subscriber(IDalamudPluginInterface pi, params Action<bool>[] actions)
=> new(pi, Label, actions);
/// <summary> Create a provider. </summary>
public static EventProvider<bool> Provider(IDalamudPluginInterface pi, IGlamourerApiState api)
=> new(pi, Label, (t => api.GPoseChanged += t, t => api.GPoseChanged -= t));
}

4
Glamourer.Api/README.md Normal file
View File

@@ -0,0 +1,4 @@
# Glamourer
This is an auxiliary repository for Glamourers external API.
For more information, see the [main repo](https://github.com/Ottermandias/Glamourer).

View File

@@ -0,0 +1,13 @@
{
"version": 1,
"dependencies": {
"net9.0-windows7.0": {
"DotNet.ReproducibleBuilds": {
"type": "Direct",
"requested": "[1.2.25, )",
"resolved": "1.2.25",
"contentHash": "xCXiw7BCxHJ8pF6wPepRUddlh2dlQlbr81gXA72hdk4FLHkKXas7EH/n+fk5UCA/YfMqG1Z6XaPiUjDbUNBUzg=="
}
}
}
}

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Penumbra-Sync
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

350
MareAPI/.gitignore vendored Normal file
View File

@@ -0,0 +1,350 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/

21
MareAPI/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Mare Synchronos
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,36 @@
using MareSynchronos.API.Data.Enum;
using MessagePack;
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Text;
using System.Security.Cryptography;
namespace MareSynchronos.API.Data;
[MessagePackObject(keyAsPropertyName: true)]
public class CharacterData
{
public CharacterData()
{
DataHash = new(() =>
{
var json = JsonSerializer.Serialize(this);
#pragma warning disable SYSLIB0021 // Type or member is obsolete
using SHA256CryptoServiceProvider cryptoProvider = new();
#pragma warning restore SYSLIB0021 // Type or member is obsolete
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(json))).Replace("-", "", StringComparison.Ordinal);
});
}
public Dictionary<ObjectKind, string> CustomizePlusData { get; set; } = new();
[JsonIgnore]
public Lazy<string> DataHash { get; }
public Dictionary<ObjectKind, List<FileReplacementData>> FileReplacements { get; set; } = new();
public Dictionary<ObjectKind, string> GlamourerData { get; set; } = new();
public string HeelsData { get; set; } = string.Empty;
public string HonorificData { get; set; } = string.Empty;
public string ManipulationData { get; set; } = string.Empty;
public string MoodlesData { get; set; } = string.Empty;
public string PetNamesData { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,11 @@
using MessagePack;
namespace MareSynchronos.API.Data;
[MessagePackObject(keyAsPropertyName: true)]
public record ChatMessage
{
public string SenderName { get; set; } = string.Empty;
public uint SenderHomeWorldId { get; set; } = 0;
public byte[] PayloadContent { get; set; } = [];
}

View File

@@ -0,0 +1,19 @@
namespace MareSynchronos.API.Data.Comparer;
public class GroupDataComparer : IEqualityComparer<GroupData>
{
public static GroupDataComparer Instance => _instance;
private static GroupDataComparer _instance = new GroupDataComparer();
private GroupDataComparer() { }
public bool Equals(GroupData? x, GroupData? y)
{
if (x == null || y == null) return false;
return x.GID.Equals(y.GID, StringComparison.Ordinal);
}
public int GetHashCode(GroupData obj)
{
return obj.GID.GetHashCode();
}
}

View File

@@ -0,0 +1,23 @@
using MareSynchronos.API.Dto.Group;
namespace MareSynchronos.API.Data.Comparer;
public class GroupDtoComparer : IEqualityComparer<GroupDto>
{
public static GroupDtoComparer Instance => _instance;
private static GroupDtoComparer _instance = new GroupDtoComparer();
private GroupDtoComparer() { }
public bool Equals(GroupDto? x, GroupDto? y)
{
if (x == null || y == null) return false;
return x.GID.Equals(y.GID, StringComparison.Ordinal);
}
public int GetHashCode(GroupDto obj)
{
return obj.Group.GID.GetHashCode();
}
}

View File

@@ -0,0 +1,20 @@
using MareSynchronos.API.Dto.Group;
namespace MareSynchronos.API.Data.Comparer;
public class GroupPairDtoComparer : IEqualityComparer<GroupPairDto>
{
public static GroupPairDtoComparer Instance => _instance;
private static GroupPairDtoComparer _instance = new();
private GroupPairDtoComparer() { }
public bool Equals(GroupPairDto? x, GroupPairDto? y)
{
if (x == null || y == null) return false;
return x.GID.Equals(y.GID, StringComparison.Ordinal) && x.UID.Equals(y.UID, StringComparison.Ordinal);
}
public int GetHashCode(GroupPairDto obj)
{
return HashCode.Combine(obj.Group.GID.GetHashCode(), obj.User.UID.GetHashCode());
}
}

View File

@@ -0,0 +1,20 @@
namespace MareSynchronos.API.Data.Comparer;
public class UserDataComparer : IEqualityComparer<UserData>
{
public static UserDataComparer Instance => _instance;
private static UserDataComparer _instance = new();
private UserDataComparer() { }
public bool Equals(UserData? x, UserData? y)
{
if (x == null || y == null) return false;
return x.UID.Equals(y.UID, StringComparison.Ordinal);
}
public int GetHashCode(UserData obj)
{
return obj.UID.GetHashCode();
}
}

View File

@@ -0,0 +1,20 @@
using MareSynchronos.API.Dto.User;
namespace MareSynchronos.API.Data.Comparer;
public class UserDtoComparer : IEqualityComparer<UserDto>
{
public static UserDtoComparer Instance => _instance;
private static UserDtoComparer _instance = new();
private UserDtoComparer() { }
public bool Equals(UserDto? x, UserDto? y)
{
if (x == null || y == null) return false;
return x.User.UID.Equals(y.User.UID, StringComparison.Ordinal);
}
public int GetHashCode(UserDto obj)
{
return obj.User.UID.GetHashCode();
}
}

View File

@@ -0,0 +1,11 @@
namespace MareSynchronos.API.Data.Enum;
[Flags]
public enum GroupPermissions
{
NoneSet = 0x0,
DisableAnimations = 0x1,
DisableSounds = 0x2,
DisableInvites = 0x4,
DisableVFX = 0x8,
}

View File

@@ -0,0 +1,9 @@
namespace MareSynchronos.API.Data.Enum;
[Flags]
public enum GroupUserInfo
{
None = 0x0,
IsModerator = 0x2,
IsPinned = 0x4
}

View File

@@ -0,0 +1,11 @@
namespace MareSynchronos.API.Data.Enum;
[Flags]
public enum GroupUserPermissions
{
NoneSet = 0x0,
Paused = 0x1,
DisableAnimations = 0x2,
DisableSounds = 0x4,
DisableVFX = 0x8,
}

View File

@@ -0,0 +1,8 @@
namespace MareSynchronos.API.Data.Enum;
public enum MessageSeverity
{
Information,
Warning,
Error
}

View File

@@ -0,0 +1,9 @@
namespace MareSynchronos.API.Data.Enum;
public enum ObjectKind
{
Player = 0,
MinionOrMount = 1,
Companion = 2,
Pet = 3,
}

View File

@@ -0,0 +1,12 @@
namespace MareSynchronos.API.Data.Enum;
[Flags]
public enum UserPermissions
{
NoneSet = 0,
Paired = 1,
Paused = 2,
DisableAnimations = 4,
DisableSounds = 8,
DisableVFX = 16,
}

View File

@@ -0,0 +1,50 @@
using MareSynchronos.API.Data.Enum;
namespace MareSynchronos.API.Data.Extensions;
public static class GroupPermissionsExtensions
{
public static bool IsDisableAnimations(this GroupPermissions perm)
{
return perm.HasFlag(GroupPermissions.DisableAnimations);
}
public static bool IsDisableSounds(this GroupPermissions perm)
{
return perm.HasFlag(GroupPermissions.DisableSounds);
}
public static bool IsDisableInvites(this GroupPermissions perm)
{
return perm.HasFlag(GroupPermissions.DisableInvites);
}
public static bool IsDisableVFX(this GroupPermissions perm)
{
return perm.HasFlag(GroupPermissions.DisableVFX);
}
public static void SetDisableAnimations(this ref GroupPermissions perm, bool set)
{
if (set) perm |= GroupPermissions.DisableAnimations;
else perm &= ~GroupPermissions.DisableAnimations;
}
public static void SetDisableSounds(this ref GroupPermissions perm, bool set)
{
if (set) perm |= GroupPermissions.DisableSounds;
else perm &= ~GroupPermissions.DisableSounds;
}
public static void SetDisableInvites(this ref GroupPermissions perm, bool set)
{
if (set) perm |= GroupPermissions.DisableInvites;
else perm &= ~GroupPermissions.DisableInvites;
}
public static void SetDisableVFX(this ref GroupPermissions perm, bool set)
{
if (set) perm |= GroupPermissions.DisableVFX;
else perm &= ~GroupPermissions.DisableVFX;
}
}

View File

@@ -0,0 +1,28 @@
using MareSynchronos.API.Data.Enum;
namespace MareSynchronos.API.Data.Extensions;
public static class GroupUserInfoExtensions
{
public static bool IsModerator(this GroupUserInfo info)
{
return info.HasFlag(GroupUserInfo.IsModerator);
}
public static bool IsPinned(this GroupUserInfo info)
{
return info.HasFlag(GroupUserInfo.IsPinned);
}
public static void SetModerator(this ref GroupUserInfo info, bool isModerator)
{
if (isModerator) info |= GroupUserInfo.IsModerator;
else info &= ~GroupUserInfo.IsModerator;
}
public static void SetPinned(this ref GroupUserInfo info, bool isPinned)
{
if (isPinned) info |= GroupUserInfo.IsPinned;
else info &= ~GroupUserInfo.IsPinned;
}
}

View File

@@ -0,0 +1,50 @@
using MareSynchronos.API.Data.Enum;
namespace MareSynchronos.API.Data.Extensions;
public static class GroupUserPermissionsExtensions
{
public static bool IsDisableAnimations(this GroupUserPermissions perm)
{
return perm.HasFlag(GroupUserPermissions.DisableAnimations);
}
public static bool IsDisableSounds(this GroupUserPermissions perm)
{
return perm.HasFlag(GroupUserPermissions.DisableSounds);
}
public static bool IsPaused(this GroupUserPermissions perm)
{
return perm.HasFlag(GroupUserPermissions.Paused);
}
public static bool IsDisableVFX(this GroupUserPermissions perm)
{
return perm.HasFlag(GroupUserPermissions.DisableVFX);
}
public static void SetDisableAnimations(this ref GroupUserPermissions perm, bool set)
{
if (set) perm |= GroupUserPermissions.DisableAnimations;
else perm &= ~GroupUserPermissions.DisableAnimations;
}
public static void SetDisableSounds(this ref GroupUserPermissions perm, bool set)
{
if (set) perm |= GroupUserPermissions.DisableSounds;
else perm &= ~GroupUserPermissions.DisableSounds;
}
public static void SetPaused(this ref GroupUserPermissions perm, bool set)
{
if (set) perm |= GroupUserPermissions.Paused;
else perm &= ~GroupUserPermissions.Paused;
}
public static void SetDisableVFX(this ref GroupUserPermissions perm, bool set)
{
if (set) perm |= GroupUserPermissions.DisableVFX;
else perm &= ~GroupUserPermissions.DisableVFX;
}
}

View File

@@ -0,0 +1,61 @@
using MareSynchronos.API.Data.Enum;
namespace MareSynchronos.API.Data.Extensions;
public static class UserPermissionsExtensions
{
public static bool IsPaired(this UserPermissions perm)
{
return perm.HasFlag(UserPermissions.Paired);
}
public static bool IsPaused(this UserPermissions perm)
{
return perm.HasFlag(UserPermissions.Paused);
}
public static bool IsDisableAnimations(this UserPermissions perm)
{
return perm.HasFlag(UserPermissions.DisableAnimations);
}
public static bool IsDisableSounds(this UserPermissions perm)
{
return perm.HasFlag(UserPermissions.DisableSounds);
}
public static bool IsDisableVFX(this UserPermissions perm)
{
return perm.HasFlag(UserPermissions.DisableVFX);
}
public static void SetPaired(this ref UserPermissions perm, bool paired)
{
if (paired) perm |= UserPermissions.Paired;
else perm &= ~UserPermissions.Paired;
}
public static void SetPaused(this ref UserPermissions perm, bool paused)
{
if (paused) perm |= UserPermissions.Paused;
else perm &= ~UserPermissions.Paused;
}
public static void SetDisableAnimations(this ref UserPermissions perm, bool set)
{
if (set) perm |= UserPermissions.DisableAnimations;
else perm &= ~UserPermissions.DisableAnimations;
}
public static void SetDisableSounds(this ref UserPermissions perm, bool set)
{
if (set) perm |= UserPermissions.DisableSounds;
else perm &= ~UserPermissions.DisableSounds;
}
public static void SetDisableVFX(this ref UserPermissions perm, bool set)
{
if (set) perm |= UserPermissions.DisableVFX;
else perm &= ~UserPermissions.DisableVFX;
}
}

View File

@@ -0,0 +1,30 @@
using MessagePack;
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Text;
using System.Security.Cryptography;
namespace MareSynchronos.API.Data;
[MessagePackObject(keyAsPropertyName: true)]
public class FileReplacementData
{
public FileReplacementData()
{
DataHash = new(() =>
{
var json = JsonSerializer.Serialize(this);
#pragma warning disable SYSLIB0021 // Type or member is obsolete
using SHA256CryptoServiceProvider cryptoProvider = new();
#pragma warning restore SYSLIB0021 // Type or member is obsolete
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(json))).Replace("-", "", StringComparison.Ordinal);
});
}
[JsonIgnore]
public Lazy<string> DataHash { get; }
public string[] GamePaths { get; set; } = Array.Empty<string>();
public string Hash { get; set; } = string.Empty;
public string FileSwapPath { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,10 @@
using MessagePack;
namespace MareSynchronos.API.Data;
[MessagePackObject(keyAsPropertyName: true)]
public record GroupData(string GID, string? Alias = null)
{
[IgnoreMember]
public string AliasOrGID => string.IsNullOrWhiteSpace(Alias) ? GID : Alias;
}

View File

@@ -0,0 +1,14 @@
using MessagePack;
namespace MareSynchronos.API.Data;
[MessagePackObject(keyAsPropertyName: true)]
public record SignedChatMessage(ChatMessage Message, UserData Sender) : ChatMessage(Message)
{
// Sender and timestamp are set by the server
public UserData Sender { get; set; } = Sender;
public long Timestamp { get; set; } = 0;
// Signature is generated by the server as SHA256(Sender.UID | Timestamp | Destination | Message)
// Where Destination is either the receiver's UID, or the group GID
public string Signature { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,10 @@
using MessagePack;
namespace MareSynchronos.API.Data;
[MessagePackObject(keyAsPropertyName: true)]
public record UserData(string UID, string? Alias = null)
{
[IgnoreMember]
public string AliasOrUID => string.IsNullOrWhiteSpace(Alias) ? UID : Alias;
}

View File

@@ -0,0 +1,12 @@
using MessagePack;
namespace MareSynchronos.API.Dto.Account;
[MessagePackObject(keyAsPropertyName: true)]
public record RegisterReplyDto
{
public bool Success { get; set; } = false;
public string ErrorMessage { get; set; } = string.Empty;
public string UID { get; set; } = string.Empty;
public string SecretKey { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,11 @@
using MessagePack;
namespace MareSynchronos.API.Dto.Account;
[MessagePackObject(keyAsPropertyName: true)]
public record RegisterReplyV2Dto
{
public bool Success { get; set; } = false;
public string ErrorMessage { get; set; } = string.Empty;
public string UID { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,11 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto;
[MessagePackObject(keyAsPropertyName: true)]
public record AuthReplyDto
{
public string Token { get; set; } = string.Empty;
public string? WellKnown { get; set; }
}

View File

@@ -0,0 +1,9 @@
namespace MareSynchronos.API.Dto.CharaData;
public enum AccessTypeDto
{
Individuals,
ClosePairs,
AllPairs,
Public
}

View File

@@ -0,0 +1,14 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto.CharaData;
[MessagePackObject(keyAsPropertyName: true)]
public record CharaDataDownloadDto(string Id, UserData Uploader) : CharaDataDto(Id, Uploader)
{
public string GlamourerData { get; init; } = string.Empty;
public string CustomizeData { get; init; } = string.Empty;
public string ManipulationData { get; set; } = string.Empty;
public List<GamePathEntry> FileGamePaths { get; init; } = [];
public List<GamePathEntry> FileSwaps { get; init; } = [];
}

View File

@@ -0,0 +1,9 @@
using MareSynchronos.API.Data;
namespace MareSynchronos.API.Dto.CharaData;
public record CharaDataDto(string Id, UserData Uploader)
{
public string Description { get; init; } = string.Empty;
public DateTime UpdatedDate { get; init; }
}

View File

@@ -0,0 +1,88 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto.CharaData;
[MessagePackObject(keyAsPropertyName: true)]
public record CharaDataFullDto(string Id, UserData Uploader) : CharaDataDto(Id, Uploader)
{
public DateTime CreatedDate { get; init; }
public DateTime ExpiryDate { get; set; }
public string GlamourerData { get; set; } = string.Empty;
public string CustomizeData { get; set; } = string.Empty;
public string ManipulationData { get; set; } = string.Empty;
public int DownloadCount { get; set; } = 0;
public List<UserData> AllowedUsers { get; set; } = [];
public List<GroupData> AllowedGroups { get; set; } = [];
public List<GamePathEntry> FileGamePaths { get; set; } = [];
public List<GamePathEntry> FileSwaps { get; set; } = [];
public List<GamePathEntry> OriginalFiles { get; set; } = [];
public AccessTypeDto AccessType { get; set; }
public ShareTypeDto ShareType { get; set; }
public List<PoseEntry> PoseData { get; set; } = [];
}
[MessagePackObject(keyAsPropertyName: true)]
public record GamePathEntry(string HashOrFileSwap, string GamePath);
[MessagePackObject(keyAsPropertyName: true)]
public record PoseEntry(long? Id)
{
public string? Description { get; set; } = string.Empty;
public string? PoseData { get; set; } = string.Empty;
public WorldData? WorldData { get; set; }
}
[MessagePackObject]
public record struct WorldData
{
[Key(0)] public LocationInfo LocationInfo { get; set; }
[Key(1)] public float PositionX { get; set; }
[Key(2)] public float PositionY { get; set; }
[Key(3)] public float PositionZ { get; set; }
[Key(4)] public float RotationX { get; set; }
[Key(5)] public float RotationY { get; set; }
[Key(6)] public float RotationZ { get; set; }
[Key(7)] public float RotationW { get; set; }
[Key(8)] public float ScaleX { get; set; }
[Key(9)] public float ScaleY { get; set; }
[Key(10)] public float ScaleZ { get; set; }
}
[MessagePackObject]
public record struct LocationInfo
{
[Key(0)] public uint ServerId { get; set; }
[Key(1)] public uint MapId { get; set; }
[Key(2)] public uint TerritoryId { get; set; }
[Key(3)] public uint DivisionId { get; set; }
[Key(4)] public uint WardId { get; set; }
[Key(5)] public uint HouseId { get; set; }
[Key(6)] public uint RoomId { get; set; }
}
[MessagePackObject]
public record struct PoseData
{
[Key(0)] public bool IsDelta { get; set; }
[Key(1)] public Dictionary<string, BoneData> Bones { get; set; }
[Key(2)] public Dictionary<string, BoneData> MainHand { get; set; }
[Key(3)] public Dictionary<string, BoneData> OffHand { get; set; }
[Key(4)] public BoneData ModelDifference { get; set; }
}
[MessagePackObject]
public record struct BoneData
{
[Key(0)] public bool Exists { get; set; }
[Key(1)] public float PositionX { get; set; }
[Key(2)] public float PositionY { get; set; }
[Key(3)] public float PositionZ { get; set; }
[Key(4)] public float RotationX { get; set; }
[Key(5)] public float RotationY { get; set; }
[Key(6)] public float RotationZ { get; set; }
[Key(7)] public float RotationW { get; set; }
[Key(8)] public float ScaleX { get; set; }
[Key(9)] public float ScaleY { get; set; }
[Key(10)] public float ScaleZ { get; set; }
}

View File

@@ -0,0 +1,11 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto.CharaData;
[MessagePackObject(keyAsPropertyName: true)]
public record CharaDataMetaInfoDto(string Id, UserData Uploader) : CharaDataDto(Id, Uploader)
{
public bool CanBeDownloaded { get; init; }
public List<PoseEntry> PoseData { get; set; } = [];
}

View File

@@ -0,0 +1,20 @@
using MessagePack;
namespace MareSynchronos.API.Dto.CharaData;
[MessagePackObject(keyAsPropertyName: true)]
public record CharaDataUpdateDto(string Id)
{
public string? Description { get; set; }
public DateTime? ExpiryDate { get; set; }
public string? GlamourerData { get; set; }
public string? CustomizeData { get; set; }
public string? ManipulationData { get; set; }
public List<string>? AllowedUsers { get; set; }
public List<string>? AllowedGroups { get; set; }
public List<GamePathEntry>? FileGamePaths { get; set; }
public List<GamePathEntry>? FileSwaps { get; set; }
public AccessTypeDto? AccessType { get; set; }
public ShareTypeDto? ShareType { get; set; }
public List<PoseEntry>? Poses { get; set; }
}

View File

@@ -0,0 +1,7 @@
namespace MareSynchronos.API.Dto.CharaData;
public enum ShareTypeDto
{
Private,
Shared
}

View File

@@ -0,0 +1,13 @@
using MareSynchronos.API.Data;
using MareSynchronos.API.Dto.Group;
using MareSynchronos.API.Dto.User;
using MessagePack;
namespace MareSynchronos.API.Dto.Chat;
[MessagePackObject(keyAsPropertyName: true)]
public record GroupChatMsgDto(GroupDto Group, SignedChatMessage Message)
{
public GroupDto Group = Group;
public SignedChatMessage Message = Message;
}

View File

@@ -0,0 +1,11 @@
using MareSynchronos.API.Data;
using MareSynchronos.API.Dto.User;
using MessagePack;
namespace MareSynchronos.API.Dto.Chat;
[MessagePackObject(keyAsPropertyName: true)]
public record UserChatMsgDto(SignedChatMessage Message)
{
public SignedChatMessage Message = Message;
}

View File

@@ -0,0 +1,25 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto;
[MessagePackObject(keyAsPropertyName: true)]
public record ConnectionDto(UserData User)
{
public Version CurrentClientVersion { get; set; } = new(0, 0, 0);
public int ServerVersion { get; set; }
public bool IsAdmin { get; set; }
public bool IsModerator { get; set; }
public ServerInfo ServerInfo { get; set; } = new();
}
[MessagePackObject(keyAsPropertyName: true)]
public record ServerInfo
{
public string ShardName { get; set; } = string.Empty;
public int MaxGroupUserCount { get; set; }
public int MaxGroupsCreatedByUser { get; set; }
public int MaxGroupsJoinedByUser { get; set; }
public Uri FileServerAddress { get; set; } = new Uri("http://nonemptyuri");
public int MaxCharaData { get; set; }
}

View File

@@ -0,0 +1,14 @@
using MessagePack;
namespace MareSynchronos.API.Dto.Files;
[MessagePackObject(keyAsPropertyName: true)]
public record DownloadFileDto : ITransferFileDto
{
public bool FileExists { get; set; } = true;
public string Hash { get; set; } = string.Empty;
public string Url { get; set; } = string.Empty;
public long Size { get; set; } = 0;
public bool IsForbidden { get; set; } = false;
public string ForbiddenBy { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MareSynchronos.API.Dto.Files;
public class FilesSendDto
{
public List<string> FileHashes { get; set; } = new();
public List<string> UIDs { get; set; } = new();
}

View File

@@ -0,0 +1,8 @@
namespace MareSynchronos.API.Dto.Files;
public interface ITransferFileDto
{
string Hash { get; set; }
bool IsForbidden { get; set; }
string ForbiddenBy { get; set; }
}

View File

@@ -0,0 +1,11 @@
using MessagePack;
namespace MareSynchronos.API.Dto.Files;
[MessagePackObject(keyAsPropertyName: true)]
public record UploadFileDto : ITransferFileDto
{
public string Hash { get; set; } = string.Empty;
public bool IsForbidden { get; set; } = false;
public string ForbiddenBy { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,19 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto.Group;
[MessagePackObject(keyAsPropertyName: true)]
public record BannedGroupUserDto : GroupPairDto
{
public BannedGroupUserDto(GroupData group, UserData user, string reason, DateTime bannedOn, string bannedBy) : base(group, user)
{
Reason = reason;
BannedOn = bannedOn;
BannedBy = bannedBy;
}
public string Reason { get; set; }
public DateTime BannedOn { get; set; }
public string BannedBy { get; set; }
}

View File

@@ -0,0 +1,13 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto.Group;
[MessagePackObject(keyAsPropertyName: true)]
public record GroupDto(GroupData Group)
{
public GroupData Group { get; set; } = Group;
public string GID => Group.GID;
public string? GroupAlias => Group.Alias;
public string GroupAliasOrGID => Group.AliasOrGID;
}

View File

@@ -0,0 +1,12 @@
using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum;
using MessagePack;
namespace MareSynchronos.API.Dto.Group;
[MessagePackObject(keyAsPropertyName: true)]
public record GroupFullInfoDto(GroupData Group, UserData Owner, GroupPermissions GroupPermissions, GroupUserPermissions GroupUserPermissions, GroupUserInfo GroupUserInfo) : GroupInfoDto(Group, Owner, GroupPermissions)
{
public GroupUserPermissions GroupUserPermissions { get; set; } = GroupUserPermissions;
public GroupUserInfo GroupUserInfo { get; set; } = GroupUserInfo;
}

View File

@@ -0,0 +1,16 @@
using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum;
using MessagePack;
namespace MareSynchronos.API.Dto.Group;
[MessagePackObject(keyAsPropertyName: true)]
public record GroupInfoDto(GroupData Group, UserData Owner, GroupPermissions GroupPermissions) : GroupDto(Group)
{
public GroupPermissions GroupPermissions { get; set; } = GroupPermissions;
public UserData Owner { get; set; } = Owner;
public string OwnerUID => Owner.UID;
public string? OwnerAlias => Owner.Alias;
public string OwnerAliasOrUID => Owner.AliasOrUID;
}

View File

@@ -0,0 +1,12 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto.Group;
[MessagePackObject(keyAsPropertyName: true)]
public record GroupPairDto(GroupData Group, UserData User) : GroupDto(Group)
{
public string UID => User.UID;
public string? UserAlias => User.Alias;
public string UserAliasOrUID => User.AliasOrUID;
}

View File

@@ -0,0 +1,12 @@
using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum;
using MessagePack;
namespace MareSynchronos.API.Dto.Group;
[MessagePackObject(keyAsPropertyName: true)]
public record GroupPairFullInfoDto(GroupData Group, UserData User, GroupUserInfo GroupPairStatusInfo, GroupUserPermissions GroupUserPermissions) : GroupPairDto(Group, User)
{
public GroupUserInfo GroupPairStatusInfo { get; set; } = GroupPairStatusInfo;
public GroupUserPermissions GroupUserPermissions { get; set; } = GroupUserPermissions;
}

View File

@@ -0,0 +1,8 @@
using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum;
using MessagePack;
namespace MareSynchronos.API.Dto.Group;
[MessagePackObject(keyAsPropertyName: true)]
public record GroupPairUserInfoDto(GroupData Group, UserData User, GroupUserInfo GroupUserInfo) : GroupPairDto(Group, User);

View File

@@ -0,0 +1,8 @@
using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum;
using MessagePack;
namespace MareSynchronos.API.Dto.Group;
[MessagePackObject(keyAsPropertyName: true)]
public record GroupPairUserPermissionDto(GroupData Group, UserData User, GroupUserPermissions GroupPairPermissions) : GroupPairDto(Group, User);

View File

@@ -0,0 +1,7 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto.Group;
[MessagePackObject(keyAsPropertyName: true)]
public record GroupPasswordDto(GroupData Group, string Password) : GroupDto(Group);

View File

@@ -0,0 +1,8 @@
using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum;
using MessagePack;
namespace MareSynchronos.API.Dto.Group;
[MessagePackObject(keyAsPropertyName: true)]
public record GroupPermissionDto(GroupData Group, GroupPermissions Permissions) : GroupDto(Group);

View File

@@ -0,0 +1,9 @@
using MessagePack;
namespace MareSynchronos.API.Dto;
[MessagePackObject(keyAsPropertyName: true)]
public record SystemInfoDto
{
public int OnlineUsers { get; set; }
}

View File

@@ -0,0 +1,7 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto.User;
[MessagePackObject(keyAsPropertyName: true)]
public record OnlineUserCharaDataDto(UserData User, CharacterData CharaData) : UserDto(User);

View File

@@ -0,0 +1,7 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto.User;
[MessagePackObject(keyAsPropertyName: true)]
public record OnlineUserIdentDto(UserData User, string Ident) : UserDto(User);

View File

@@ -0,0 +1,7 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto.User;
[MessagePackObject(keyAsPropertyName: true)]
public record UserCharaDataMessageDto(List<UserData> Recipients, CharacterData CharaData);

View File

@@ -0,0 +1,7 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto.User;
[MessagePackObject(keyAsPropertyName: true)]
public record UserDto(UserData User);

View File

@@ -0,0 +1,12 @@
using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum;
using MessagePack;
namespace MareSynchronos.API.Dto.User;
[MessagePackObject(keyAsPropertyName: true)]
public record UserPairDto(UserData User, UserPermissions OwnPermissions, UserPermissions OtherPermissions) : UserDto(User)
{
public UserPermissions OwnPermissions { get; set; } = OwnPermissions;
public UserPermissions OtherPermissions { get; set; } = OtherPermissions;
}

View File

@@ -0,0 +1,8 @@
using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum;
using MessagePack;
namespace MareSynchronos.API.Dto.User;
[MessagePackObject(keyAsPropertyName: true)]
public record UserPermissionsDto(UserData User, UserPermissions Permissions) : UserDto(User);

View File

@@ -0,0 +1,7 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto.User;
[MessagePackObject(keyAsPropertyName: true)]
public record UserProfileDto(UserData User, bool Disabled, bool? IsNSFW, string? ProfilePictureBase64, string? Description) : UserDto(User);

View File

@@ -0,0 +1,7 @@
using MareSynchronos.API.Data;
using MessagePack;
namespace MareSynchronos.API.Dto.User;
[MessagePackObject(keyAsPropertyName: true)]
public record UserProfileReportDto(UserData User, string ProfileReport) : UserDto(User);

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MessagePack.Annotations" Version="2.5.129" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32602.215
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronos.API", "MareSynchronos.API.csproj", "{CD05EE19-802F-4490-AAD8-CAD4BF1D630D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CD05EE19-802F-4490-AAD8-CAD4BF1D630D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD05EE19-802F-4490-AAD8-CAD4BF1D630D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD05EE19-802F-4490-AAD8-CAD4BF1D630D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD05EE19-802F-4490-AAD8-CAD4BF1D630D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DFB70C71-AB27-468D-A08B-218CA79BF69D}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,14 @@
namespace MareSynchronos.API.Routes;
public class MareAuth
{
public const string Auth = "/auth";
public const string Auth_CreateIdent = "createWithIdent";
public const string Auth_CreateIdentV2 = "createWithIdentV2";
public const string Auth_Register = "registerNewKey";
public const string Auth_RegisterV2 = "registerNewKeyV2";
public static Uri AuthFullPath(Uri baseUri) => new Uri(baseUri, Auth + "/" + Auth_CreateIdent);
public static Uri AuthV2FullPath(Uri baseUri) => new Uri(baseUri, Auth + "/" + Auth_CreateIdentV2);
public static Uri AuthRegisterFullPath(Uri baseUri) => new Uri(baseUri, Auth + "/" + Auth_Register);
public static Uri AuthRegisterV2FullPath(Uri baseUri) => new Uri(baseUri, Auth + "/" + Auth_RegisterV2);
}

View File

@@ -0,0 +1,45 @@
namespace MareSynchronos.API.Routes;
public class MareFiles
{
public const string Cache = "/cache";
public const string Cache_Get = "get";
public const string Request = "/request";
public const string Request_Cancel = "cancel";
public const string Request_Check = "check";
public const string Request_Enqueue = "enqueue";
public const string Request_RequestFile = "file";
public const string ServerFiles = "/files";
public const string ServerFiles_DeleteAll = "deleteAll";
public const string ServerFiles_FilesSend = "filesSend";
public const string ServerFiles_GetSizes = "getFileSizes";
public const string ServerFiles_Upload = "upload";
public const string ServerFiles_UploadRaw = "uploadRaw";
public const string ServerFiles_UploadMunged = "uploadMunged";
public const string Distribution = "/dist";
public const string Distribution_Get = "get";
public const string Main = "/main";
public const string Main_SendReady = "sendReady";
public static Uri CacheGetFullPath(Uri baseUri, Guid requestId) => new(baseUri, Cache + "/" + Cache_Get + "?requestId=" + requestId.ToString());
public static Uri RequestCancelFullPath(Uri baseUri, Guid guid) => new Uri(baseUri, Request + "/" + Request_Cancel + "?requestId=" + guid.ToString());
public static Uri RequestCheckQueueFullPath(Uri baseUri, Guid guid) => new Uri(baseUri, Request + "/" + Request_Check + "?requestId=" + guid.ToString());
public static Uri RequestEnqueueFullPath(Uri baseUri) => new(baseUri, Request + "/" + Request_Enqueue);
public static Uri RequestRequestFileFullPath(Uri baseUri, string hash) => new(baseUri, Request + "/" + Request_RequestFile + "?file=" + hash);
public static Uri ServerFilesDeleteAllFullPath(Uri baseUri) => new(baseUri, ServerFiles + "/" + ServerFiles_DeleteAll);
public static Uri ServerFilesFilesSendFullPath(Uri baseUri) => new(baseUri, ServerFiles + "/" + ServerFiles_FilesSend);
public static Uri ServerFilesGetSizesFullPath(Uri baseUri) => new(baseUri, ServerFiles + "/" + ServerFiles_GetSizes);
public static Uri ServerFilesUploadFullPath(Uri baseUri, string hash) => new(baseUri, ServerFiles + "/" + ServerFiles_Upload + "/" + hash);
public static Uri ServerFilesUploadRawFullPath(Uri baseUri, string hash) => new(baseUri, ServerFiles + "/" + ServerFiles_UploadRaw + "/" + hash);
public static Uri ServerFilesUploadMunged(Uri baseUri, string hash) => new(baseUri, ServerFiles + "/" + ServerFiles_UploadMunged + "/" + hash);
public static Uri DistributionGetFullPath(Uri baseUri, string hash) => new(baseUri, Distribution + "/" + Distribution_Get + "?file=" + hash);
public static Uri MainSendReadyFullPath(Uri baseUri, string uid, Guid request) => new(baseUri, Main + "/" + Main_SendReady + "/" + "?uid=" + uid + "&requestId=" + request.ToString());
}

Some files were not shown because too many files have changed in this diff Show More