├── .github └── workflows │ ├── build-and-test.yaml │ ├── manual-release.yaml │ └── publish-nuget.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── NetStone.GameData.Lumina ├── LuminaGameDataProvider.cs ├── NetStone.GameData.Lumina.csproj └── README.md ├── NetStone.GameData.Packs ├── Internal │ ├── AchievementTable.cs │ ├── ClassJobTable.cs │ ├── DeityTable.cs │ ├── GrandCompanyTable.cs │ ├── ItemTable.cs │ ├── MinionTable.cs │ ├── MountTable.cs │ ├── RaceTable.cs │ ├── ReputationTable.cs │ ├── TitleTable.cs │ ├── TownTable.cs │ └── TribeTable.cs ├── NetStone.GameData.Packs.csproj └── PacksGameDataProvider.cs ├── NetStone.Test ├── NetStone.Test.csproj └── Tests.cs ├── NetStone.sln ├── NetStone ├── Changelog.md ├── Constants.cs ├── Definitions │ ├── .editorconfig │ ├── DefinitionsContainer.cs │ ├── DefinitionsPack.cs │ ├── Model │ │ ├── CWLS │ │ │ ├── CrossworldLinkshellDefinition.cs │ │ │ ├── CrossworldLinkshellMemberDefinition.cs │ │ │ └── CrossworldLinkshellSearchDefinition.cs │ │ ├── Character │ │ │ ├── CharacterAchievementDefinition.cs │ │ │ ├── CharacterAttributesDefinition.cs │ │ │ ├── CharacterClassJobDefinition.cs │ │ │ ├── CharacterCollectableDefinition.cs │ │ │ ├── CharacterDefinition.cs │ │ │ ├── CharacterGearDefinition.cs │ │ │ └── CharacterSearchDefinition.cs │ │ ├── FreeCompany │ │ │ ├── FreeCompanyDefinition.cs │ │ │ ├── FreeCompanyFocusDefinition.cs │ │ │ ├── FreeCompanyMembersEntryDefinition.cs │ │ │ ├── FreeCompanyReputationDefinition.cs │ │ │ └── FreeCompanySearchDefinition.cs │ │ ├── IDefinition.cs │ │ ├── IconLayersDefinition.cs │ │ ├── Linkshell │ │ │ ├── LinkshellDefinition.cs │ │ │ ├── LinkshellMemberDefinition.cs │ │ │ └── LinkshellSearchEntryDefinition.cs │ │ ├── MetaDefinition.cs │ │ └── PagedDefinition.cs │ └── XivApiDefinitionsContainer.cs ├── GameData │ ├── GameDataInfo.cs │ ├── GenderedGameData.cs │ ├── IGameDataProvider.cs │ ├── LanguageStrings.cs │ ├── NamedGameData.cs │ └── TitleGameData.cs ├── LodestoneClient.cs ├── Model │ ├── IOptionalParseable.cs │ ├── IPaginatedResult.cs │ ├── LodestoneParseable.cs │ └── Parseables │ │ ├── CWLS │ │ ├── LodestoneCrossworldLinkshell.cs │ │ └── Members │ │ │ └── CrossworldLinkshellMemberEntry.cs │ │ ├── Character │ │ ├── Achievement │ │ │ ├── CharacterAchievementEntry.cs │ │ │ └── CharacterAchievementPage.cs │ │ ├── CharacterAttributes.cs │ │ ├── ClassJob │ │ │ ├── CharacterClassJob.cs │ │ │ ├── ClassJobBozja.cs │ │ │ ├── ClassJobEntry.cs │ │ │ └── ClassJobEureka.cs │ │ ├── Collectable │ │ │ ├── CharacterCollectable.cs │ │ │ └── CharacterCollectableEntry.cs │ │ ├── FreeCompanySocialGroup.cs │ │ ├── Gear │ │ │ ├── CharacterGear.cs │ │ │ ├── GearEntry.cs │ │ │ └── SoulcrystalEntry.cs │ │ └── LodestoneCharacter.cs │ │ ├── FreeCompany │ │ ├── FreeCompanyEstate.cs │ │ ├── FreeCompanyFocus.cs │ │ ├── FreeCompanyFocusEntry.cs │ │ ├── FreeCompanyReputation.cs │ │ ├── FreeCompanyReputationEntry.cs │ │ ├── LodestoneFreeCompany.cs │ │ └── Members │ │ │ ├── FreeCompanyMembers.cs │ │ │ └── FreeCompanyMembersEntry.cs │ │ ├── IconLayers.cs │ │ ├── Linkshell │ │ ├── LodestoneLinkshell.cs │ │ └── Members │ │ │ └── LinkshellMemberEntry.cs │ │ ├── Search │ │ ├── CWLS │ │ │ ├── CrossworldLinkshellSearchEntry.cs │ │ │ └── CrossworldLinkshellSearchPage.cs │ │ ├── Character │ │ │ ├── CharacterSearchEntry.cs │ │ │ └── CharacterSearchPage.cs │ │ ├── FreeCompany │ │ │ ├── FreeCompanySearchEntry.cs │ │ │ └── FreeCompanySearchPage.cs │ │ └── Linkshell │ │ │ ├── LinkshellSearchEntry.cs │ │ │ └── LinkshellSearchPage.cs │ │ └── SocialGroup.cs ├── NetStone.csproj ├── NetStone.xml ├── Search │ ├── Character │ │ ├── CharacterSearchQuery.cs │ │ └── SortKind.cs │ ├── FreeCompany │ │ ├── ActiveMembers.cs │ │ ├── ActiveTimes.cs │ │ ├── Focus.cs │ │ ├── FreeCompanySearchQuery.cs │ │ ├── Housing.cs │ │ ├── Recruitment.cs │ │ ├── Seeking.cs │ │ └── SortKind.cs │ ├── ISearchQuery.cs │ └── Linkshell │ │ └── LinkshellSearchQuery.cs ├── StaticData │ ├── ClassJob.cs │ ├── GrandCompany.cs │ ├── Language.cs │ ├── Race.cs │ ├── Role.cs │ └── Tribe.cs └── UserAgent.cs ├── README.md ├── compile-fbs.sh └── patch-flatbuffers.sh /.github/workflows/build-and-test.yaml: -------------------------------------------------------------------------------- 1 | name: Build & Test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | with: 11 | submodules: recursive 12 | - name: Setup .NET 13 | uses: actions/setup-dotnet@v4 14 | with: 15 | dotnet-version: | 16 | 8.0.x 17 | 6.0.x 18 | 3.1.x 19 | - name: Install dependencies 20 | run: dotnet restore 21 | - name: Build 22 | run: dotnet build --configuration Release --no-restore 23 | - name: Test 24 | run: dotnet test --no-restore --verbosity normal 25 | -------------------------------------------------------------------------------- /.github/workflows/manual-release.yaml: -------------------------------------------------------------------------------- 1 | name: Manual Publish (NuGet.org) 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | release-netstone: 6 | type: boolean 7 | required: true 8 | version: 9 | type: string 10 | release-lumina: 11 | type: boolean 12 | required: true 13 | lumina-version: 14 | type: string 15 | jobs: 16 | publish: 17 | runs-on: ubuntu-latest 18 | env: 19 | EXPECTED_VERSION: ${{inputs.version }} 20 | EXPECTED_LUMINA_VERSION: ${{inputs.lumina-version }} 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | submodules: recursive 25 | - name: Setup .NET 26 | uses: actions/setup-dotnet@v4 27 | with: 28 | dotnet-version: 8.0.x 29 | - name: Restore dependencies 30 | run: dotnet restore 31 | - name: Build 32 | run: dotnet build --configuration Release --no-restore 33 | - name: Test 34 | run: dotnet test --configuration Release --no-build --verbosity normal 35 | - name: Package 36 | run: dotnet pack --configuration Release --no-build 37 | - name: Push package NetStone 38 | if: ${{inputs.release-netstone}} 39 | run: dotnet nuget push "**/Release/NetStone.$EXPECTED_VERSION.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json 40 | - name: Push package NetStone.GameData.Lumina 41 | if: ${{inputs.release-lumina}} 42 | run: dotnet nuget push "**/Release/NetStone.GameData.Lumina.$EXPECTED_LUMINA_VERSION.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json 43 | -------------------------------------------------------------------------------- /.github/workflows/publish-nuget.yml: -------------------------------------------------------------------------------- 1 | name: Package and Publish (NuGet.org) 2 | on: 3 | push: 4 | branches: 5 | - master 6 | tags-ignore: 7 | - '*' # don't loop 8 | 9 | jobs: 10 | tag: 11 | runs-on: ubuntu-latest 12 | outputs: 13 | result: ${{steps.s1.outputs.TAG_RESULT}} 14 | version: ${{steps.s1.outputs.TAG_VERSION}} 15 | steps: 16 | - name: Determine Tag 17 | id: s1 18 | run: | 19 | version=$(echo "${{github.event.head_commit.message}}" | perl -nle 'm/build:\s*([0-9]+.[0-9]+.[0-9]+)/; print $1'); 20 | 21 | if [ -z "$version" ]; 22 | then 23 | echo "No build version found"; 24 | echo "TAG_RESULT=fail" >> $GITHUB_OUTPUT; 25 | else 26 | export TAG_VERSION=$version; 27 | echo "TAG_VERSION=$TAG_VERSION" >> $GITHUB_OUTPUT; 28 | echo "TAG_RESULT=pass" >> $GITHUB_OUTPUT; 29 | fi 30 | - name: Create Tag 31 | if: steps.s1.outputs.TAG_RESULT == 'pass' 32 | uses: actions/github-script@v3 33 | with: 34 | github-token: ${{ github.token }} 35 | script: | 36 | github.git.createRef({ 37 | owner: context.repo.owner, 38 | repo: context.repo.repo, 39 | ref: "refs/tags/v${{steps.s1.outputs.TAG_VERSION}}", 40 | sha: context.sha 41 | }) 42 | tag-lumina: 43 | runs-on: ubuntu-latest 44 | outputs: 45 | result: ${{steps.s1.outputs.TAG_RESULT}} 46 | version: ${{steps.s1.outputs.TAG_VERSION}} 47 | steps: 48 | - name: Determine Tag 49 | id: s1 50 | run: | 51 | version=$(echo "${{github.event.head_commit.message}}" | perl -nle 'm/build-lumina:\s*([0-9]+.[0-9]+.[0-9]+)/; print $1'); 52 | 53 | if [ -z "$version" ]; 54 | then 55 | echo "No build version found"; 56 | echo "TAG_RESULT=fail" >> $GITHUB_OUTPUT; 57 | else 58 | export TAG_VERSION=$version; 59 | echo "TAG_VERSION=$TAG_VERSION" >> $GITHUB_OUTPUT; 60 | echo "TAG_RESULT=pass" >> $GITHUB_OUTPUT; 61 | fi 62 | - name: Create Tag 63 | if: steps.s1.outputs.TAG_RESULT == 'pass' 64 | uses: actions/github-script@v3 65 | with: 66 | github-token: ${{ github.token }} 67 | script: | 68 | github.git.createRef({ 69 | owner: context.repo.owner, 70 | repo: context.repo.repo, 71 | ref: "refs/tags/lumina-v${{steps.s1.outputs.TAG_VERSION}}", 72 | sha: context.sha 73 | }) 74 | publish: 75 | runs-on: ubuntu-latest 76 | needs: [tag, tag-lumina] 77 | if: ${{needs.tag.outputs.result == 'pass' || needs.tag-lumina.outputs.result == 'pass'}} 78 | env: 79 | EXPECTED_VERSION: ${{needs.tag.outputs.version }} 80 | EXPECTED_LUMINA_VERSION: ${{needs.tag-lumina.outputs.version }} 81 | steps: 82 | - uses: actions/checkout@v4 83 | with: 84 | submodules: recursive 85 | - name: Setup .NET 86 | uses: actions/setup-dotnet@v4 87 | with: 88 | dotnet-version: 8.0.x 89 | - name: Restore dependencies 90 | run: dotnet restore 91 | - name: Build 92 | run: dotnet build --configuration Release --no-restore 93 | - name: Test 94 | run: dotnet test --configuration Release --no-build --verbosity normal 95 | - name: Package 96 | run: dotnet pack --configuration Release --no-build 97 | - name: Push package NetStone 98 | if: ${{needs.tag.outputs.result == 'pass'}} 99 | run: dotnet nuget push "**/Release/NetStone.$EXPECTED_VERSION.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json 100 | - name: Push package NetStone.GameData.Lumina 101 | if: ${{needs.tag-lumina.outputs.result == 'pass'}} 102 | run: dotnet nuget push "**/Release/NetStone.GameData.Lumina.$EXPECTED_LUMINA_VERSION.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json 103 | 104 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/lodestone-data-exports"] 2 | path = lib/lodestone-data-exports 3 | url = https://github.com/karashiiro/lodestone-data-exports 4 | [submodule "lib/flatbuffers"] 5 | path = lib/flatbuffers 6 | url = https://github.com/google/flatbuffers.git 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 goaaats, Koenari 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /NetStone.GameData.Lumina/LuminaGameDataProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Lumina; 4 | using Lumina.Data; 5 | using Lumina.Excel; 6 | using Lumina.Excel.GeneratedSheets; 7 | using Cyalume = Lumina.GameData; 8 | 9 | namespace NetStone.GameData.Lumina; 10 | 11 | /// 12 | /// Game data provider that reads data directly from Lumina. 13 | /// This is slow and just exists as an alternative to the flatbuffer-based solution. 14 | /// 15 | public class LuminaGameDataProvider : IGameDataProvider 16 | { 17 | private readonly Cyalume lumina; 18 | 19 | /// 20 | /// Create an instance of LuminaGameDataProvider 21 | /// 22 | /// Path to game installation 23 | public LuminaGameDataProvider(DirectoryInfo gamePath) 24 | { 25 | this.lumina = new Cyalume(gamePath.FullName, new LuminaOptions{PanicOnSheetChecksumMismatch = false}); 26 | } 27 | 28 | /// 29 | /// Gets infromation of an item by it's name 30 | /// 31 | /// Name of the item to search 32 | /// of the item with supplied name 33 | public NamedGameData? GetItem(string name) 34 | { 35 | var item = FindRow(name); 36 | 37 | if (item == null) 38 | return null; 39 | 40 | var langs = CollectLanguages(item.RowId); 41 | 42 | return new NamedGameData 43 | { 44 | Info = new GameDataInfo 45 | { 46 | Key = item.RowId, 47 | Name = name, 48 | }, 49 | 50 | Name = new LanguageStrings 51 | { 52 | En = langs.En.Name, 53 | De = langs.De.Name, 54 | Fr = langs.Fr.Name, 55 | Ja = langs.Ja.Name, 56 | }, 57 | }; 58 | } 59 | 60 | private (T En, T De, T Fr, T Ja) CollectLanguages(uint key) where T : ExcelRow 61 | { 62 | var en = this.lumina.Excel.GetSheet(Language.English); 63 | var de = this.lumina.Excel.GetSheet(Language.English); 64 | var fr = this.lumina.Excel.GetSheet(Language.English); 65 | var ja = this.lumina.Excel.GetSheet(Language.English); 66 | 67 | return (en!.GetRow(key)!, de!.GetRow(key)!, fr!.GetRow(key)!, ja!.GetRow(key)!); 68 | } 69 | 70 | private T? FindRow(string name) where T: ExcelRow 71 | { 72 | var en = this.lumina.Excel.GetSheet(Language.English); 73 | var de = this.lumina.Excel.GetSheet(Language.English); 74 | var fr = this.lumina.Excel.GetSheet(Language.English); 75 | var ja = this.lumina.Excel.GetSheet(Language.English); 76 | 77 | var res = FindRowInSheet(en, name); 78 | if (res != null) 79 | return res; 80 | 81 | res = FindRowInSheet(de, name); 82 | if (res != null) 83 | return res; 84 | 85 | res = FindRowInSheet(fr, name); 86 | if (res != null) 87 | return res; 88 | 89 | res = FindRowInSheet(ja, name); 90 | return res; 91 | } 92 | 93 | private static T? FindRowInSheet(ExcelSheet? sheet, string name) where T: ExcelRow 94 | { 95 | if (sheet == null) 96 | return null; 97 | 98 | foreach (var excelRow in sheet) 99 | { 100 | if (excelRow is Item item) 101 | { 102 | if (item.Name.ToString().Equals(name, StringComparison.InvariantCultureIgnoreCase)) 103 | return excelRow; 104 | } 105 | } 106 | 107 | return null; 108 | } 109 | } -------------------------------------------------------------------------------- /NetStone.GameData.Lumina/NetStone.GameData.Lumina.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | NetStone.GameData.Lumina 7 | 1.1.0 8 | 1.1.0 9 | Lumina game data bindings for NetStone. 10 | goaaats 11 | https://github.com/xivapi/NetStone 12 | https://github.com/xivapi/NetStone 13 | 14 | LICENSE 15 | README.md 16 | 17 | true 18 | true 19 | 20 | 10.0 21 | enable 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | true 35 | true 36 | snupkg 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /NetStone.GameData.Lumina/README.md: -------------------------------------------------------------------------------- 1 | # NetStone.GameData.Lumina 2 | 3 | Lumina game data bindings for NetStone. -------------------------------------------------------------------------------- /NetStone.GameData.Packs/Internal/ItemTable.cs: -------------------------------------------------------------------------------- 1 | // 2 | // automatically generated by the FlatBuffers compiler, do not modify 3 | // 4 | 5 | namespace FFXIV 6 | { 7 | 8 | using global::System; 9 | using global::System.Collections.Generic; 10 | using global::FlatBuffers; 11 | 12 | public struct ItemTable : IFlatbufferObject 13 | { 14 | private Table __p; 15 | public ByteBuffer ByteBuffer { get { return __p.bb; } } 16 | public static void ValidateVersion() { FlatBufferConstants.FLATBUFFERS_2_0_0(); } 17 | public static ItemTable GetRootAsItemTable(ByteBuffer _bb) { return GetRootAsItemTable(_bb, new ItemTable()); } 18 | public static ItemTable GetRootAsItemTable(ByteBuffer _bb, ItemTable obj) { return (obj.__assign(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); } 19 | public void __init(int _i, ByteBuffer _bb) { __p = new Table(_i, _bb); } 20 | public ItemTable __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; } 21 | 22 | public FFXIV.Item? Items(int j) { int o = __p.__offset(4); return o != 0 ? (FFXIV.Item?)(new FFXIV.Item()).__assign(__p.__indirect(__p.__vector(o) + j * 4), __p.bb) : null; } 23 | public int ItemsLength { get { int o = __p.__offset(4); return o != 0 ? __p.__vector_len(o) : 0; } } 24 | public FFXIV.Item? ItemsByKey(uint key) { int o = __p.__offset(4); return o != 0 ? FFXIV.Item.__lookup_by_key(__p.__vector(o), key, __p.bb) : null; } 25 | 26 | public static Offset CreateItemTable(FlatBufferBuilder builder, 27 | VectorOffset ItemsOffset = default(VectorOffset)) { 28 | builder.StartTable(1); 29 | ItemTable.AddItems(builder, ItemsOffset); 30 | return ItemTable.EndItemTable(builder); 31 | } 32 | 33 | public static void StartItemTable(FlatBufferBuilder builder) { builder.StartTable(1); } 34 | public static void AddItems(FlatBufferBuilder builder, VectorOffset ItemsOffset) { builder.AddOffset(0, ItemsOffset.Value, 0); } 35 | public static VectorOffset CreateItemsVector(FlatBufferBuilder builder, Offset[] data) { builder.StartVector(4, data.Length, 4); for (int i = data.Length - 1; i >= 0; i--) builder.AddOffset(data[i].Value); return builder.EndVector(); } 36 | public static VectorOffset CreateItemsVectorBlock(FlatBufferBuilder builder, Offset[] data) { builder.StartVector(4, data.Length, 4); builder.Add(data); return builder.EndVector(); } 37 | public static void StartItemsVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(4, numElems, 4); } 38 | public static Offset EndItemTable(FlatBufferBuilder builder) { 39 | int o = builder.EndTable(); 40 | return new Offset(o); 41 | } 42 | public static void FinishItemTableBuffer(FlatBufferBuilder builder, Offset offset) { builder.Finish(offset.Value); } 43 | public static void FinishSizePrefixedItemTableBuffer(FlatBufferBuilder builder, Offset offset) { builder.FinishSizePrefixed(offset.Value); } 44 | }; 45 | 46 | public struct Item : IFlatbufferObject 47 | { 48 | private Table __p; 49 | public ByteBuffer ByteBuffer { get { return __p.bb; } } 50 | public static void ValidateVersion() { FlatBufferConstants.FLATBUFFERS_2_0_0(); } 51 | public static Item GetRootAsItem(ByteBuffer _bb) { return GetRootAsItem(_bb, new Item()); } 52 | public static Item GetRootAsItem(ByteBuffer _bb, Item obj) { return (obj.__assign(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); } 53 | public void __init(int _i, ByteBuffer _bb) { __p = new Table(_i, _bb); } 54 | public Item __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; } 55 | 56 | public uint Id { get { int o = __p.__offset(4); return o != 0 ? __p.bb.GetUint(o + __p.bb_pos) : (uint)0; } } 57 | public string NameEn { get { int o = __p.__offset(6); return o != 0 ? __p.__string(o + __p.bb_pos) : null; } } 58 | #if ENABLE_SPAN_T 59 | public Span GetNameEnBytes() { return __p.__vector_as_span(6, 1); } 60 | #else 61 | public ArraySegment? GetNameEnBytes() { return __p.__vector_as_arraysegment(6); } 62 | #endif 63 | public byte[] GetNameEnArray() { return __p.__vector_as_array(6); } 64 | public string NameFr { get { int o = __p.__offset(8); return o != 0 ? __p.__string(o + __p.bb_pos) : null; } } 65 | #if ENABLE_SPAN_T 66 | public Span GetNameFrBytes() { return __p.__vector_as_span(8, 1); } 67 | #else 68 | public ArraySegment? GetNameFrBytes() { return __p.__vector_as_arraysegment(8); } 69 | #endif 70 | public byte[] GetNameFrArray() { return __p.__vector_as_array(8); } 71 | public string NameDe { get { int o = __p.__offset(10); return o != 0 ? __p.__string(o + __p.bb_pos) : null; } } 72 | #if ENABLE_SPAN_T 73 | public Span GetNameDeBytes() { return __p.__vector_as_span(10, 1); } 74 | #else 75 | public ArraySegment? GetNameDeBytes() { return __p.__vector_as_arraysegment(10); } 76 | #endif 77 | public byte[] GetNameDeArray() { return __p.__vector_as_array(10); } 78 | public string NameJa { get { int o = __p.__offset(12); return o != 0 ? __p.__string(o + __p.bb_pos) : null; } } 79 | #if ENABLE_SPAN_T 80 | public Span GetNameJaBytes() { return __p.__vector_as_span(12, 1); } 81 | #else 82 | public ArraySegment? GetNameJaBytes() { return __p.__vector_as_arraysegment(12); } 83 | #endif 84 | public byte[] GetNameJaArray() { return __p.__vector_as_array(12); } 85 | 86 | public static Offset CreateItem(FlatBufferBuilder builder, 87 | uint Id = 0, 88 | StringOffset NameEnOffset = default(StringOffset), 89 | StringOffset NameFrOffset = default(StringOffset), 90 | StringOffset NameDeOffset = default(StringOffset), 91 | StringOffset NameJaOffset = default(StringOffset)) { 92 | builder.StartTable(5); 93 | Item.AddNameJa(builder, NameJaOffset); 94 | Item.AddNameDe(builder, NameDeOffset); 95 | Item.AddNameFr(builder, NameFrOffset); 96 | Item.AddNameEn(builder, NameEnOffset); 97 | Item.AddId(builder, Id); 98 | return Item.EndItem(builder); 99 | } 100 | 101 | public static void StartItem(FlatBufferBuilder builder) { builder.StartTable(5); } 102 | public static void AddId(FlatBufferBuilder builder, uint Id) { builder.AddUint(0, Id, 0); } 103 | public static void AddNameEn(FlatBufferBuilder builder, StringOffset NameEnOffset) { builder.AddOffset(1, NameEnOffset.Value, 0); } 104 | public static void AddNameFr(FlatBufferBuilder builder, StringOffset NameFrOffset) { builder.AddOffset(2, NameFrOffset.Value, 0); } 105 | public static void AddNameDe(FlatBufferBuilder builder, StringOffset NameDeOffset) { builder.AddOffset(3, NameDeOffset.Value, 0); } 106 | public static void AddNameJa(FlatBufferBuilder builder, StringOffset NameJaOffset) { builder.AddOffset(4, NameJaOffset.Value, 0); } 107 | public static Offset EndItem(FlatBufferBuilder builder) { 108 | int o = builder.EndTable(); 109 | return new Offset(o); 110 | } 111 | 112 | public static VectorOffset CreateSortedVectorOfItem(FlatBufferBuilder builder, Offset[] offsets) { 113 | Array.Sort(offsets, (Offset o1, Offset o2) => builder.DataBuffer.GetUint(Table.__offset(4, o1.Value, builder.DataBuffer)).CompareTo(builder.DataBuffer.GetUint(Table.__offset(4, o2.Value, builder.DataBuffer)))); 114 | return builder.CreateVectorOfTables(offsets); 115 | } 116 | 117 | public static Item? __lookup_by_key(int vectorLocation, uint key, ByteBuffer bb) { 118 | int span = bb.GetInt(vectorLocation - 4); 119 | int start = 0; 120 | while (span != 0) { 121 | int middle = span / 2; 122 | int tableOffset = Table.__indirect(vectorLocation + 4 * (start + middle), bb); 123 | int comp = bb.GetUint(Table.__offset(4, bb.Length - tableOffset, bb)).CompareTo(key); 124 | if (comp > 0) { 125 | span = middle; 126 | } else if (comp < 0) { 127 | middle++; 128 | start += middle; 129 | span -= middle; 130 | } else { 131 | return new Item().__assign(tableOffset, bb); 132 | } 133 | } 134 | return null; 135 | } 136 | }; 137 | 138 | 139 | } 140 | -------------------------------------------------------------------------------- /NetStone.GameData.Packs/NetStone.GameData.Packs.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /NetStone.GameData.Packs/PacksGameDataProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using FFXIV; 4 | using FlatBuffers; 5 | 6 | namespace NetStone.GameData.Packs 7 | { 8 | public class PacksGameDataProvider : IGameDataProvider 9 | { 10 | private readonly ItemTable items; 11 | 12 | private PacksGameDataProvider(string path) 13 | { 14 | items = ItemTable.GetRootAsItemTable(LoadByteBuffer(Path.Combine(path, "item_table.bin"))); 15 | } 16 | 17 | public NamedGameData? GetItem(string name) 18 | { 19 | var lower = name.ToLower().Replace("", string.Empty); 20 | 21 | for (var i = 0; i < items.ItemsLength; i++) 22 | { 23 | var item = items.Items(i)!.Value; 24 | 25 | if (lower.Equals(item.NameEn, StringComparison.InvariantCultureIgnoreCase) 26 | || lower.Equals(item.NameDe, StringComparison.InvariantCultureIgnoreCase) 27 | || lower.Equals(item.NameFr, StringComparison.InvariantCultureIgnoreCase) 28 | || lower.Equals(item.NameJa, StringComparison.InvariantCultureIgnoreCase)) 29 | { 30 | return new NamedGameData 31 | { 32 | Info = new GameDataInfo 33 | { 34 | Key = item.Id, 35 | Name = name, 36 | }, 37 | 38 | Name = new LanguageStrings 39 | { 40 | En = item.NameEn, 41 | De = item.NameDe, 42 | Fr = item.NameFr, 43 | Ja = item.NameJa, 44 | }, 45 | }; 46 | } 47 | } 48 | 49 | return null; 50 | } 51 | 52 | private ByteBuffer LoadByteBuffer(string file) 53 | { 54 | var bytes = File.ReadAllBytes(file); 55 | return new ByteBuffer(bytes); 56 | } 57 | 58 | public static PacksGameDataProvider Load(DirectoryInfo path) => new PacksGameDataProvider(path.FullName); 59 | } 60 | } -------------------------------------------------------------------------------- /NetStone.Test/NetStone.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | false 7 | 10.0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /NetStone.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30717.126 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetStone", "NetStone\NetStone.csproj", "{F1BA8D83-A58B-43EF-9B52-C6880CE8DBB6}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetStone.Test", "NetStone.Test\NetStone.Test.csproj", "{9753064B-BB14-46D8-9447-51A0B4AD7E02}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlatBuffers", "lib\flatbuffers\net\FlatBuffers\FlatBuffers.csproj", "{32F1B5DC-9E73-483C-B7FD-48EF97F2AA34}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetStone.GameData.Packs", "NetStone.GameData.Packs\NetStone.GameData.Packs.csproj", "{D415EE52-ADB2-4B93-9B88-79D3A34A494E}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetStone.GameData.Lumina", "NetStone.GameData.Lumina\NetStone.GameData.Lumina.csproj", "{6B3EB70B-127D-4B5B-83E2-E0660A0F684A}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {F1BA8D83-A58B-43EF-9B52-C6880CE8DBB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {F1BA8D83-A58B-43EF-9B52-C6880CE8DBB6}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {F1BA8D83-A58B-43EF-9B52-C6880CE8DBB6}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {F1BA8D83-A58B-43EF-9B52-C6880CE8DBB6}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {9753064B-BB14-46D8-9447-51A0B4AD7E02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {9753064B-BB14-46D8-9447-51A0B4AD7E02}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {9753064B-BB14-46D8-9447-51A0B4AD7E02}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {9753064B-BB14-46D8-9447-51A0B4AD7E02}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {32F1B5DC-9E73-483C-B7FD-48EF97F2AA34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {32F1B5DC-9E73-483C-B7FD-48EF97F2AA34}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {32F1B5DC-9E73-483C-B7FD-48EF97F2AA34}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {32F1B5DC-9E73-483C-B7FD-48EF97F2AA34}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {D415EE52-ADB2-4B93-9B88-79D3A34A494E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {D415EE52-ADB2-4B93-9B88-79D3A34A494E}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {D415EE52-ADB2-4B93-9B88-79D3A34A494E}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {D415EE52-ADB2-4B93-9B88-79D3A34A494E}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {6B3EB70B-127D-4B5B-83E2-E0660A0F684A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {6B3EB70B-127D-4B5B-83E2-E0660A0F684A}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {6B3EB70B-127D-4B5B-83E2-E0660A0F684A}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {6B3EB70B-127D-4B5B-83E2-E0660A0F684A}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(ExtensibilityGlobals) = postSolution 47 | SolutionGuid = {BFEDBEEC-7831-4158-A1BF-B9FDCB780AFB} 48 | EndGlobalSection 49 | EndGlobal 50 | -------------------------------------------------------------------------------- /NetStone/Changelog.md: -------------------------------------------------------------------------------- 1 | ## 1.4.1 2 | ### Behavioral changes 3 | - Exceptions from http requests are not caught and ignored anymore 4 | ### Fixes 5 | - Fixed FC search parameter for recruiting 6 | ### Contributors 7 | - Tawmy 8 | ## 1.4.0 9 | ### License 10 | - Switched to MIT license 11 | ### New Features 12 | - Added 13 | - LinkShells 14 | - Linkshell search 15 | - CWLS 16 | - CWLS search 17 | ## 1.3.1 18 | ### Bugfixes 19 | - Free Company search 20 | - Fixed queries with empty name 21 | - Fixed Active Times 22 | ### Contributors 23 | - Tawmy 24 | ## 1.3.0 25 | ### Breaking Changes 26 | - AttackMagicPotency in Character Attributes is now nullable 27 | - HealingMagicPotency in Character Attributes is now nullable 28 | ### New Features 29 | - Character Attributes 30 | - Added Craftsmanship 31 | - Added Control 32 | - Added Gathering 33 | - Added Perception 34 | - Free Company Member 35 | - Added Free Company Rank 36 | - Added Free Company Rank Icon 37 | - Gear 38 | - Added item level 39 | ### Contributors 40 | - Tawmy 41 | - Corielljacob 42 | ## 1.2.1 43 | ### New Features 44 | - Character 45 | - Added Bozja (credit to twobe7) 46 | - Added Eureka (credit to twobe7) 47 | ## 1.2.0 48 | ### Breaking Changes 49 | - ClassJobs are now non-nullable. Use IsUnlocked instead of a null check 50 | ### Fixes 51 | - Character achievements should now work as intended 52 | - Added new jobs (credit to Tawmy) 53 | ### New Features 54 | - Character 55 | - Added Tribe 56 | - Added Clan 57 | - Added Gender 58 | ## 1.1.2 59 | ### Fixes 60 | - FreeCompany 61 | - Fix exception when no ranking is present 62 | ## 1.1.1 63 | ### Fixes 64 | - Social Group 65 | - Exists works as intended now 66 | - Id returns null when the group does not exist 67 | ## 1.1.0 68 | ### New Features 69 | - Item HQ status 70 | - Game data updated for 6.4 71 | ### Fixes 72 | - Character 73 | - Level of active job 74 | - Materia 75 | - Job experience 76 | - Skill Speed 77 | - Achievement details 78 | - Free Company 79 | - Active State 80 | - Leveling focus 81 | ### Technical changes 82 | - Using C# built-in nullability (remove JetBrains annotations) 83 | ## 1.0.0 84 | Initial Release -------------------------------------------------------------------------------- /NetStone/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone; 2 | 3 | /// 4 | /// Contains constants used for initializing the Lodestone parser. 5 | /// 6 | public class Constants 7 | { 8 | /// 9 | /// Base URL for the Lodestone website. 10 | /// 11 | public const string LodestoneBase = "https://eu.finalfantasyxiv.com"; 12 | } -------------------------------------------------------------------------------- /NetStone/Definitions/.editorconfig: -------------------------------------------------------------------------------- 1 | #Ignore nullabilty for classes purely created by JSON deserialization 2 | [*.cs] 3 | dotnet_diagnostic.CS8618.severity = none -------------------------------------------------------------------------------- /NetStone/Definitions/DefinitionsContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using NetStone.Definitions.Model; 4 | using NetStone.Definitions.Model.Character; 5 | using NetStone.Definitions.Model.CWLS; 6 | using NetStone.Definitions.Model.FreeCompany; 7 | using NetStone.Definitions.Model.Linkshell; 8 | 9 | namespace NetStone.Definitions; 10 | 11 | /// 12 | /// Class providing definitions(Selectors, paths) for parsing lodestone content. 13 | /// 14 | public abstract class DefinitionsContainer : IDisposable 15 | { 16 | #region Definitions 17 | 18 | /// 19 | /// Meta definitions 20 | /// Contains version, user-agents and Uris 21 | /// 22 | public MetaDefinition Meta { get; protected set; } 23 | 24 | /// 25 | /// General definitions for characters 26 | /// 27 | public CharacterDefinition Character { get; protected set; } 28 | 29 | /// 30 | /// Class information for characters 31 | /// 32 | public CharacterClassJobDefinition ClassJob { get; protected set; } 33 | 34 | /// 35 | /// Gear definitions for character 36 | /// 37 | public CharacterGearDefinition Gear { get; protected set; } 38 | 39 | /// 40 | /// Definitions for a character's attribute 41 | /// 42 | public CharacterAttributesDefinition Attributes { get; set; } 43 | 44 | /// 45 | /// Definitions for a character's achievements 46 | /// 47 | public CharacterAchievementDefinition Achievement { get; set; } 48 | 49 | /// 50 | /// Definitions for a character's mounts 51 | /// 52 | public CharacterCollectableDefinition Mount { get; set; } 53 | 54 | /// 55 | /// Definitions for a character's minions 56 | /// 57 | public CharacterCollectableDefinition Minion { get; set; } 58 | 59 | /// 60 | /// Definitions for Free Company 61 | /// 62 | public FreeCompanyDefinition FreeCompany { get; set; } 63 | 64 | /// 65 | /// Definitions for Free Company focus 66 | /// 67 | public FreeCompanyFocusDefinition FreeCompanyFocus { get; set; } 68 | 69 | /// 70 | /// Definitions for Free Company reputation 71 | /// 72 | public FreeCompanyReputationDefinition FreeCompanyReputation { get; set; } 73 | 74 | /// 75 | /// Definitions for Free Company member list 76 | /// 77 | public PagedDefinition FreeCompanyMembers { get; set; } 78 | 79 | /// 80 | /// Definitions for character search 81 | /// 82 | public PagedDefinition CharacterSearch { get; protected set; } 83 | 84 | /// 85 | /// Definitions for Free company search 86 | /// 87 | public PagedDefinition FreeCompanySearch { get; protected set; } 88 | 89 | /// 90 | /// Definitions for cross world link shells 91 | /// 92 | public CrossworldLinkshellDefinition CrossworldLinkshell { get; protected set; } 93 | 94 | /// 95 | /// Definitions for cross world link shell members 96 | /// 97 | public PagedDefinition CrossworldLinkshellMember { get; protected set; } 98 | 99 | /// 100 | /// Definitions for cross world link shell searches 101 | /// 102 | public PagedDefinition CrossworldLinkshellSearch { get; protected set; } 103 | 104 | /// 105 | /// Definitions for link shells 106 | /// 107 | public LinkshellDefinition Linkshell { get; protected set; } 108 | 109 | /// 110 | /// Definitions for link shell members 111 | /// 112 | public PagedDefinition LinkshellMember { get; protected set; } 113 | 114 | /// 115 | /// Definitions for link-shell searches 116 | /// 117 | public PagedDefinition LinkshellSearch { get; protected set; } 118 | 119 | #endregion 120 | 121 | /// 122 | /// Loads the definitions from repo 123 | /// 124 | /// Reload task 125 | public abstract Task Reload(); 126 | 127 | /// 128 | public abstract void Dispose(); 129 | } -------------------------------------------------------------------------------- /NetStone/Definitions/DefinitionsPack.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace NetStone.Definitions; 5 | 6 | /// 7 | /// Models the definition on how to extract information from the Lodestone HTML document 8 | /// 9 | public class DefinitionsPack 10 | { 11 | /// 12 | /// Hold a CSS selector to get the relevant html node from the DOM 13 | /// 14 | [JsonProperty("selector")] 15 | public string Selector { get; set; } 16 | 17 | /// 18 | /// A perl regex to extract the relevant information from the inner text of the relevant node 19 | /// 20 | [JsonProperty("regex")] 21 | public string? PerlBasedRegex { get; set; } 22 | 23 | /// 24 | /// C# usable regex based on 25 | /// 26 | public string? Regex => this.PerlBasedRegex?.Replace("(?P<", "(?<", StringComparison.InvariantCulture); 27 | 28 | //Never used 29 | //[JsonProperty("or")] public string Description { get; set; } 30 | 31 | /// 32 | /// HTML attribute that holds information. Only set if data is not in the inner text 33 | /// 34 | [JsonProperty("attribute")] 35 | public string? Attribute { get; set; } 36 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/CWLS/CrossworldLinkshellDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.CWLS; 4 | 5 | /// 6 | /// Definitions for cross world link shell 7 | /// 8 | public class CrossworldLinkshellDefinition : IDefinition 9 | { 10 | /// 11 | /// Name 12 | /// 13 | [JsonProperty("NAME")] 14 | public DefinitionsPack Name { get; set; } 15 | 16 | /// 17 | /// Name 18 | /// 19 | [JsonProperty("DC")] 20 | public DefinitionsPack DataCenter { get; set; } 21 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/CWLS/CrossworldLinkshellMemberDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.CWLS; 4 | 5 | 6 | /// 7 | /// 8 | /// 9 | public class CrossworldLinkshellMemberEntryDefinition : PagedEntryDefinition 10 | { 11 | /// 12 | /// Avatar 13 | /// 14 | [JsonProperty("AVATAR")] public DefinitionsPack Avatar { get; set; } 15 | 16 | /// 17 | /// ID 18 | /// 19 | [JsonProperty("ID")] public DefinitionsPack Id { get; set; } 20 | 21 | /// 22 | /// Name 23 | /// 24 | [JsonProperty("NAME")] public DefinitionsPack Name { get; set; } 25 | 26 | /// 27 | /// Rank 28 | /// 29 | [JsonProperty("RANK")] public DefinitionsPack Rank { get; set; } 30 | 31 | /// 32 | /// Rank Icon 33 | /// 34 | [JsonProperty("RANK_ICON")] public DefinitionsPack RankIcon { get; set; } 35 | 36 | /// 37 | /// Linkshell rank 38 | /// 39 | [JsonProperty("LINKSHELL_RANK")] public DefinitionsPack LinkshellRank { get; set; } 40 | 41 | /// 42 | /// Linkshell rank Icon 43 | /// 44 | [JsonProperty("LINKSHELL_RANK_ICON")] public DefinitionsPack LinkshellRankIcon { get; set; } 45 | 46 | /// 47 | /// Server 48 | /// 49 | [JsonProperty("SERVER")] public DefinitionsPack Server { get; set; } 50 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/CWLS/CrossworldLinkshellSearchDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.CWLS; 4 | /// 5 | /// Definition container for one Cross World Link Shell search result entry 6 | /// 7 | public class CrossworldLinkshellSearchEntryDefinition : PagedEntryDefinition 8 | { 9 | /// 10 | /// ID 11 | /// 12 | [JsonProperty("ID")] public DefinitionsPack Id { get; set; } 13 | 14 | /// 15 | /// Name 16 | /// 17 | [JsonProperty("NAME")] public DefinitionsPack Name { get; set; } 18 | 19 | /// 20 | /// Rank 21 | /// 22 | [JsonProperty("DC")] public DefinitionsPack Dc { get; set; } 23 | 24 | /// 25 | /// Rank Icon 26 | /// 27 | [JsonProperty("ACTIVE_MEMBERS")] public DefinitionsPack ActiveMembers { get; set; } 28 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/Character/CharacterAchievementDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.Character; 4 | 5 | /// 6 | /// Models the definition of the achievement list 7 | /// 8 | public class CharacterAchievementDefinition : PagedDefinition 9 | { 10 | /// 11 | /// Total number of achievements earned by this character 12 | /// 13 | [JsonProperty("TOTAL_ACHIEVEMENTS")] 14 | public DefinitionsPack TotalAchievements { get; set; } 15 | 16 | /// 17 | /// Total achievement points earned by this character 18 | /// 19 | [JsonProperty("ACHIEVEMENT_POINTS")] 20 | public DefinitionsPack AchievementPoints { get; set; } 21 | 22 | /// 23 | /// Full text displayed for this achievement 24 | /// 25 | [JsonProperty("ACTIVITY_DESCRIPTION")] 26 | public DefinitionsPack ActivityDescription { get; set; } 27 | } 28 | /// 29 | /// Models the definition of one achievement entry for a character 30 | /// 31 | public class CharacterAchievementEntryDefinition : PagedEntryDefinition 32 | { 33 | /// 34 | /// Achievement name 35 | /// 36 | [JsonProperty("NAME")] 37 | public DefinitionsPack Name { get; set; } 38 | 39 | /// 40 | /// Id of this achievement 41 | /// 42 | [JsonProperty("ID")] 43 | public DefinitionsPack Id { get; set; } 44 | 45 | /// 46 | /// Time when this achievement was earned 47 | /// 48 | [JsonProperty("TIME")] 49 | public DefinitionsPack Time { get; set; } 50 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/Character/CharacterAttributesDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.Character; 4 | 5 | /// 6 | /// Node definitions for a character's attributes 7 | /// 8 | public class CharacterAttributesDefinition : IDefinition 9 | { 10 | /// 11 | /// Strength 12 | /// 13 | [JsonProperty("STRENGTH")] 14 | public DefinitionsPack Strength { get; set; } 15 | 16 | /// 17 | /// Dexterity 18 | /// 19 | [JsonProperty("DEXTERITY")] 20 | public DefinitionsPack Dexterity { get; set; } 21 | 22 | /// 23 | /// Vitality 24 | /// 25 | [JsonProperty("VITALITY")] 26 | public DefinitionsPack Vitality { get; set; } 27 | 28 | /// 29 | /// Intelligence 30 | /// 31 | [JsonProperty("INTELLIGENCE")] 32 | public DefinitionsPack Intelligence { get; set; } 33 | 34 | /// 35 | /// Mind 36 | /// 37 | [JsonProperty("MIND")] 38 | public DefinitionsPack Mind { get; set; } 39 | 40 | /// 41 | /// Critical Hit 42 | /// 43 | [JsonProperty("CRITICAL_HIT_RATE")] 44 | public DefinitionsPack CriticalHitRate { get; set; } 45 | 46 | /// 47 | /// Determination 48 | /// 49 | [JsonProperty("DETERMINATION")] 50 | public DefinitionsPack Determination { get; set; } 51 | 52 | /// 53 | /// Direct Hit 54 | /// 55 | [JsonProperty("DIRECT_HIT_RATE")] 56 | public DefinitionsPack DirectHitRate { get; set; } 57 | 58 | /// 59 | /// Defense 60 | /// 61 | [JsonProperty("DEFENSE")] 62 | public DefinitionsPack Defense { get; set; } 63 | 64 | /// 65 | /// Magic Defense 66 | /// 67 | [JsonProperty("MAGIC_DEFENSE")] 68 | public DefinitionsPack MagicDefense { get; set; } 69 | 70 | /// 71 | /// Attack Power 72 | /// 73 | [JsonProperty("ATTACK_POWER")] 74 | public DefinitionsPack AttackPower { get; set; } 75 | 76 | /// 77 | /// Skill Speed 78 | /// 79 | [JsonProperty("SKILL_SPEED")] 80 | public DefinitionsPack SkillSpeed { get; set; } 81 | 82 | /// 83 | /// Attack Magic Potency 84 | /// 85 | [JsonProperty("ATTACK_MAGIC_POTENCY")] 86 | public DefinitionsPack AttackMagicPotency { get; set; } 87 | 88 | /// 89 | /// Healing Magic Potency 90 | /// 91 | [JsonProperty("HEALING_MAGIC_POTENCY")] 92 | public DefinitionsPack HealingMagicPotency { get; set; } 93 | 94 | /// 95 | /// Spell Speed 96 | /// 97 | [JsonProperty("SPELL_SPEED")] 98 | public DefinitionsPack SpellSpeed { get; set; } 99 | 100 | /// 101 | /// Tenacity 102 | /// 103 | [JsonProperty("TENACITY")] 104 | public DefinitionsPack Tenacity { get; set; } 105 | 106 | /// 107 | /// Piety 108 | /// 109 | [JsonProperty("PIETY")] 110 | public DefinitionsPack Piety { get; set; } 111 | 112 | /// 113 | /// Hit Points 114 | /// 115 | [JsonProperty("HP")] 116 | public DefinitionsPack Hp { get; set; } 117 | 118 | /// 119 | /// MP, GP or CP depending on Job 120 | /// 121 | [JsonProperty("MP_GP_CP")] 122 | public DefinitionsPack MpGpCp { get; set; } 123 | 124 | /// 125 | /// Name of 126 | /// 127 | [JsonProperty("MP_GP_CP_PARAMETER_NAME")] 128 | public DefinitionsPack MpGpCpParameterName { get; set; } 129 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/Character/CharacterCollectableDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.Character; 4 | 5 | /// 6 | /// General class for a category of collectables 7 | /// 8 | public interface CharacterCollectableDefinition : IDefinition 9 | { 10 | /// 11 | /// Get node definition for and entry 12 | /// 13 | /// Node definition 14 | public CollectableNodeDefinition GetDefinitions(); 15 | } 16 | 17 | /// 18 | /// Definition pack for a character's mounts 19 | /// 20 | public class CharacterMountDefinition : CharacterCollectableDefinition 21 | { 22 | /// 23 | /// Node definition for mounts 24 | /// 25 | [JsonProperty("MOUNTS")] 26 | public CollectableNodeDefinition Mounts { get; set; } 27 | 28 | /// 29 | public CollectableNodeDefinition GetDefinitions() => this.Mounts; 30 | } 31 | 32 | /// 33 | /// Definition pack for a character's minions 34 | /// 35 | public class CharacterMinionDefinition : CharacterCollectableDefinition 36 | { 37 | /// 38 | /// Noe definition for minions 39 | /// 40 | [JsonProperty("MINIONS")] 41 | public CollectableNodeDefinition Minions { get; set; } 42 | 43 | /// 44 | public CollectableNodeDefinition GetDefinitions() => this.Minions; 45 | } 46 | 47 | /// 48 | /// General definition for a collectable 49 | /// 50 | public class CollectableNodeDefinition 51 | { 52 | /// 53 | /// Root node of collectable entry 54 | /// 55 | [JsonProperty("ROOT")] 56 | public DefinitionsPack Root { get; set; } 57 | 58 | /// 59 | /// Name 60 | /// 61 | [JsonProperty("NAME")] 62 | public DefinitionsPack Name { get; set; } 63 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/Character/CharacterDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.Character; 4 | 5 | /// 6 | /// Definitions for free company of character 7 | /// 8 | public class CharacterFreeCompany : ICharacterSocialGroupDefinition 9 | { 10 | /// 11 | [JsonProperty("NAME")] 12 | public DefinitionsPack Name { get; set; } 13 | 14 | /// 15 | [JsonProperty("ICON_LAYERS")] 16 | public IconLayersDefinition IconLayers { get; set; } 17 | } 18 | 19 | /// 20 | /// Definitions for PvP team of character 21 | /// 22 | public class CharacterPvPTeam : ICharacterSocialGroupDefinition 23 | { 24 | /// 25 | [JsonProperty("NAME")] 26 | public DefinitionsPack Name { get; set; } 27 | 28 | /// 29 | [JsonProperty("ICON_LAYERS")] 30 | public IconLayersDefinition IconLayers { get; set; } 31 | } 32 | 33 | /// 34 | /// General definition of a social group 35 | /// 36 | public interface ICharacterSocialGroupDefinition : IDefinition 37 | { 38 | /// 39 | /// Definition for the name of the group 40 | /// 41 | DefinitionsPack Name { get; set; } 42 | 43 | /// 44 | /// Definition of Icon Layers 45 | /// 46 | IconLayersDefinition IconLayers { get; set; } 47 | } 48 | 49 | /// 50 | /// Generic definition for icon and name combination 51 | /// 52 | public class NameIconDefinition : IDefinition 53 | { 54 | /// 55 | /// Name 56 | /// 57 | [JsonProperty("NAME")] 58 | public DefinitionsPack Name { get; set; } 59 | 60 | /// 61 | /// Icon 62 | /// 63 | [JsonProperty("ICON")] 64 | public DefinitionsPack Icon { get; set; } 65 | } 66 | 67 | /// 68 | /// Set of definition packs for character profile 69 | /// 70 | public class CharacterDefinition : IDefinition 71 | { 72 | /// 73 | /// Currently active class/job 74 | /// 75 | [JsonProperty("ACTIVE_CLASSJOB")] 76 | public DefinitionsPack ActiveClassJob { get; set; } 77 | 78 | /// 79 | /// Level of current class/job 80 | /// 81 | [JsonProperty("ACTIVE_CLASSJOB_LEVEL")] 82 | public DefinitionsPack ActiveClassJobLevel { get; set; } 83 | 84 | /// 85 | /// Characters avatar picture 86 | /// 87 | [JsonProperty("AVATAR")] 88 | public DefinitionsPack Avatar { get; set; } 89 | 90 | /// 91 | /// Players description for this character 92 | /// 93 | [JsonProperty("BIO")] 94 | public DefinitionsPack Bio { get; set; } 95 | 96 | /// 97 | /// Free Company of this character 98 | /// 99 | [JsonProperty("FREE_COMPANY")] 100 | public CharacterFreeCompany FreeCompany { get; set; } 101 | 102 | /// 103 | /// Grand company 104 | /// 105 | [JsonProperty("GRAND_COMPANY")] 106 | public DefinitionsPack GrandCompany { get; set; } 107 | 108 | /// 109 | /// Deity 110 | /// 111 | [JsonProperty("GUARDIAN_DEITY")] 112 | public NameIconDefinition GuardianDeity { get; set; } 113 | 114 | /// 115 | /// Name 116 | /// 117 | [JsonProperty("NAME")] 118 | public DefinitionsPack Name { get; set; } 119 | 120 | /// 121 | /// Nameday (Birthday) 122 | /// 123 | [JsonProperty("NAMEDAY")] 124 | public DefinitionsPack Nameday { get; set; } 125 | 126 | /// 127 | /// Image of full character 128 | /// 129 | [JsonProperty("PORTRAIT")] 130 | public DefinitionsPack Portrait { get; set; } 131 | 132 | /// 133 | /// PvP Team 134 | /// 135 | [JsonProperty("PVP_TEAM")] 136 | public CharacterPvPTeam PvPTeam { get; set; } 137 | 138 | /// 139 | /// Race, Clan and Gender of the character 140 | /// 141 | [JsonProperty("RACE_CLAN_GENDER")] 142 | public DefinitionsPack RaceClanGender { get; set; } 143 | 144 | /// 145 | /// Homeworld of the character 146 | /// 147 | [JsonProperty("SERVER")] 148 | public DefinitionsPack Server { get; set; } 149 | 150 | /// 151 | /// Title 152 | /// 153 | [JsonProperty("TITLE")] 154 | public DefinitionsPack Title { get; set; } 155 | 156 | /// 157 | /// City-State affiliation 158 | /// 159 | [JsonProperty("TOWN")] 160 | public NameIconDefinition Town { get; set; } 161 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/Character/CharacterGearDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.Character; 4 | 5 | /// 6 | /// Definitions for a slot of gear in character profile 7 | /// 8 | public class GearEntryDefinition 9 | { 10 | /// 11 | /// Name of the item 12 | /// 13 | [JsonProperty("NAME")] 14 | public DefinitionsPack Name { get; set; } 15 | 16 | /// 17 | /// Link to Eorzea Database for item 18 | /// 19 | [JsonProperty("DB_LINK")] 20 | public DefinitionsPack DbLink { get; set; } 21 | 22 | /// 23 | /// Name of glamour 24 | /// 25 | [JsonProperty("MIRAGE_NAME")] 26 | public DefinitionsPack MirageName { get; set; } 27 | 28 | /// 29 | /// Link to Eorzea Database for glamour item 30 | /// 31 | [JsonProperty("MIRAGE_DB_LINK")] 32 | public DefinitionsPack MirageDbLink { get; set; } 33 | 34 | /// 35 | /// Die of the item 36 | /// 37 | [JsonProperty("STAIN")] 38 | public DefinitionsPack Stain { get; set; } 39 | 40 | /// 41 | /// Materia Slot 1 42 | /// 43 | [JsonProperty("MATERIA_1")] 44 | public DefinitionsPack Materia1 { get; set; } 45 | 46 | /// 47 | /// Materia Slot 2 48 | /// 49 | [JsonProperty("MATERIA_2")] 50 | public DefinitionsPack Materia2 { get; set; } 51 | 52 | /// 53 | /// Materia Slot 3 54 | /// 55 | [JsonProperty("MATERIA_3")] 56 | public DefinitionsPack Materia3 { get; set; } 57 | 58 | /// 59 | /// Materia Slot 4 60 | /// 61 | [JsonProperty("MATERIA_4")] 62 | public DefinitionsPack Materia4 { get; set; } 63 | 64 | /// 65 | /// Materia Slot 5 66 | /// 67 | [JsonProperty("MATERIA_5")] 68 | public DefinitionsPack Materia5 { get; set; } 69 | 70 | /// 71 | /// Name of creator/crafter of this item (if applicable) 72 | /// 73 | [JsonProperty("CREATOR_NAME")] 74 | public DefinitionsPack CreatorName { get; set; } 75 | 76 | /// 77 | /// Item level of the item 78 | /// 79 | [JsonProperty("ITEM_LEVEL")] 80 | public DefinitionsPack ItemLevel { get; set; } 81 | } 82 | 83 | /// 84 | /// Definition for Soul Crystal slot 85 | /// 86 | public class SoulcrystalEntryDefinition 87 | { 88 | /// 89 | /// Name of the item 90 | /// 91 | [JsonProperty("NAME")] 92 | public DefinitionsPack Name { get; set; } 93 | } 94 | 95 | /// 96 | /// Definition for all gear of the character 97 | /// 98 | public class CharacterGearDefinition : IDefinition 99 | { 100 | /// 101 | /// Main hand weapon 102 | /// 103 | [JsonProperty("MAINHAND")] 104 | public GearEntryDefinition Mainhand { get; set; } 105 | 106 | /// 107 | /// Off hand weapon 108 | /// 109 | [JsonProperty("OFFHAND")] 110 | public GearEntryDefinition Offhand { get; set; } 111 | 112 | /// 113 | /// Head piece 114 | /// 115 | [JsonProperty("HEAD")] 116 | public GearEntryDefinition Head { get; set; } 117 | 118 | /// 119 | /// Chest piece 120 | /// 121 | [JsonProperty("BODY")] 122 | public GearEntryDefinition Body { get; set; } 123 | 124 | /// 125 | /// Hand piece 126 | /// 127 | [JsonProperty("HANDS")] 128 | public GearEntryDefinition Hands { get; set; } 129 | 130 | /// 131 | /// Waist 132 | /// 133 | [JsonProperty("WAIST")] 134 | public GearEntryDefinition Waist { get; set; } 135 | 136 | /// 137 | /// Legs 138 | /// 139 | [JsonProperty("LEGS")] 140 | public GearEntryDefinition Legs { get; set; } 141 | 142 | /// 143 | /// Feet 144 | /// 145 | [JsonProperty("FEET")] 146 | public GearEntryDefinition Feet { get; set; } 147 | 148 | /// 149 | /// Earrings 150 | /// 151 | [JsonProperty("EARRINGS")] 152 | public GearEntryDefinition Earrings { get; set; } 153 | 154 | /// 155 | /// Necklace 156 | /// 157 | [JsonProperty("NECKLACE")] 158 | public GearEntryDefinition Necklace { get; set; } 159 | 160 | /// 161 | /// Braccelets 162 | /// 163 | [JsonProperty("BRACELETS")] 164 | public GearEntryDefinition Bracelets { get; set; } 165 | 166 | /// 167 | /// Right ring 168 | /// 169 | [JsonProperty("RING1")] 170 | public GearEntryDefinition Ring1 { get; set; } 171 | 172 | /// 173 | /// Left ring 174 | /// 175 | [JsonProperty("RING2")] 176 | public GearEntryDefinition Ring2 { get; set; } 177 | 178 | /// 179 | /// Soul Crystal 180 | /// 181 | [JsonProperty("SOULCRYSTAL")] 182 | public SoulcrystalEntryDefinition Soulcrystal { get; set; } 183 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/Character/CharacterSearchDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.Character; 4 | 5 | /// 6 | /// Definition pack for character search results 7 | /// 8 | public class CharacterSearchEntryDefinition : PagedEntryDefinition 9 | { 10 | /// 11 | /// Avatar of character 12 | /// 13 | [JsonProperty("AVATAR")] 14 | public DefinitionsPack Avatar { get; set; } 15 | 16 | /// 17 | /// Lodestone Id 18 | /// 19 | [JsonProperty("ID")] 20 | public DefinitionsPack Id { get; set; } 21 | 22 | /// 23 | /// Language 24 | /// 25 | [JsonProperty("LANG")] 26 | public DefinitionsPack Lang { get; set; } 27 | 28 | /// 29 | /// Name 30 | /// 31 | [JsonProperty("NAME")] 32 | public DefinitionsPack Name { get; set; } 33 | 34 | /// 35 | /// Grand Company rank 36 | /// 37 | [JsonProperty("RANK")] 38 | public DefinitionsPack Rank { get; set; } 39 | 40 | /// 41 | /// Grand Company rank icon 42 | /// 43 | [JsonProperty("RANK_ICON")] 44 | public DefinitionsPack RankIcon { get; set; } 45 | 46 | /// 47 | /// Homeworld 48 | /// 49 | [JsonProperty("SERVER")] 50 | public DefinitionsPack Server { get; set; } 51 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/FreeCompany/FreeCompanyDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.FreeCompany; 4 | 5 | /// 6 | /// Definitions for FC estate 7 | /// 8 | public class EstateDefinition : IDefinition 9 | { 10 | /// 11 | /// Definition for empty estate 12 | /// 13 | [JsonProperty("NO_ESTATE")] 14 | public DefinitionsPack NoEstate { get; set; } 15 | 16 | /// 17 | /// Greeting 18 | /// 19 | [JsonProperty("GREETING")] 20 | public DefinitionsPack Greeting { get; set; } 21 | 22 | /// 23 | /// Name 24 | /// 25 | [JsonProperty("NAME")] 26 | public DefinitionsPack Name { get; set; } 27 | 28 | /// 29 | /// Plot 30 | /// 31 | [JsonProperty("PLOT")] 32 | public DefinitionsPack Plot { get; set; } 33 | } 34 | 35 | /// 36 | /// Ranking definitions 37 | /// 38 | public class Ranking 39 | { 40 | /// 41 | /// Weekly ranking 42 | /// 43 | [JsonProperty("WEEKLY")] 44 | public DefinitionsPack Weekly { get; set; } 45 | 46 | /// 47 | /// Monthly ranking 48 | /// 49 | [JsonProperty("MONTHLY")] 50 | public DefinitionsPack Monthly { get; set; } 51 | } 52 | 53 | /// 54 | /// Definitions for free company profile 55 | /// 56 | public class FreeCompanyDefinition : IDefinition 57 | { 58 | /// 59 | /// Is Active 60 | /// 61 | [JsonProperty("ACTIVE_STATE")] 62 | public DefinitionsPack Activestate { get; set; } 63 | 64 | /// 65 | /// Active member count 66 | /// 67 | [JsonProperty("ACTIVE_MEMBER_COUNT")] 68 | public DefinitionsPack ActiveMemberCount { get; set; } 69 | 70 | /// 71 | /// Layers of the crest 72 | /// 73 | [JsonProperty("CREST_LAYERS")] 74 | public IconLayersDefinition CrestLayers { get; set; } 75 | 76 | /// 77 | /// Information about estate 78 | /// 79 | [JsonProperty("ESTATE")] 80 | public EstateDefinition EstateDefinition { get; set; } 81 | 82 | /// 83 | /// Foundation date 84 | /// 85 | [JsonProperty("FORMED")] 86 | public DefinitionsPack Formed { get; set; } 87 | 88 | /// 89 | /// Grand company 90 | /// 91 | [JsonProperty("GRAND_COMPANY")] 92 | public DefinitionsPack GrandCompany { get; set; } 93 | 94 | /// 95 | /// Id 96 | /// 97 | [JsonProperty("ID")] 98 | public DefinitionsPack Id { get; set; } 99 | 100 | /// 101 | /// Name 102 | /// 103 | [JsonProperty("NAME")] 104 | public DefinitionsPack Name { get; set; } 105 | 106 | /// 107 | /// Rank 108 | /// 109 | [JsonProperty("RANK")] 110 | public DefinitionsPack Rank { get; set; } 111 | 112 | /// 113 | /// Rankings 114 | /// 115 | [JsonProperty("RANKING")] 116 | public Ranking Ranking { get; set; } 117 | 118 | /// 119 | /// Recruitment info 120 | /// 121 | [JsonProperty("RECRUITMENT")] 122 | public DefinitionsPack Recruitment { get; set; } 123 | 124 | /// 125 | /// World 126 | /// 127 | [JsonProperty("SERVER")] 128 | public DefinitionsPack Server { get; set; } 129 | 130 | /// 131 | /// Slogan 132 | /// 133 | [JsonProperty("SLOGAN")] 134 | public DefinitionsPack Slogan { get; set; } 135 | 136 | /// 137 | /// Tags 138 | /// 139 | [JsonProperty("TAG")] 140 | public DefinitionsPack Tag { get; set; } 141 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/FreeCompany/FreeCompanyFocusDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.FreeCompany; 4 | 5 | /// 6 | /// Definition for one type of FC foc 7 | /// us 8 | /// 9 | public class FreeCompanyFocusEntryDefinition : IDefinition 10 | { 11 | /// 12 | /// Name of type 13 | /// 14 | [JsonProperty("NAME")] 15 | public DefinitionsPack NAME { get; set; } 16 | 17 | /// 18 | /// Icon for type 19 | /// 20 | [JsonProperty("ICON")] 21 | public DefinitionsPack ICON { get; set; } 22 | 23 | /// 24 | /// Status (if company focuses on this) 25 | /// 26 | [JsonProperty("STATUS")] 27 | public DefinitionsPack STATUS { get; set; } 28 | } 29 | 30 | /// 31 | /// Definition container for all FC focus types 32 | /// 33 | public class FreeCompanyFocusDefinition : IDefinition 34 | { 35 | /// 36 | /// No focus specified 37 | /// 38 | [JsonProperty("NOT_SPECIFIED")] 39 | public DefinitionsPack NOTSPECIFIED { get; set; } 40 | 41 | /// 42 | /// Role play 43 | /// 44 | [JsonProperty("RP")] 45 | public FreeCompanyFocusEntryDefinition RolePlay { get; set; } 46 | 47 | /// 48 | /// Leveling 49 | /// 50 | [JsonProperty("LEVELING")] 51 | public FreeCompanyFocusEntryDefinition Leveling { get; set; } 52 | 53 | /// 54 | /// Casual 55 | /// 56 | [JsonProperty("CASUAL")] 57 | public FreeCompanyFocusEntryDefinition Casual { get; set; } 58 | 59 | /// 60 | /// Hardcore 61 | /// 62 | [JsonProperty("HARDCORE")] 63 | public FreeCompanyFocusEntryDefinition Hardcore { get; set; } 64 | 65 | /// 66 | /// Dungeons 67 | /// 68 | [JsonProperty("DUNGEONS")] 69 | public FreeCompanyFocusEntryDefinition Dungeons { get; set; } 70 | 71 | /// 72 | /// Guild hests 73 | /// 74 | [JsonProperty("GUILDHESTS")] 75 | public FreeCompanyFocusEntryDefinition Guildhests { get; set; } 76 | 77 | /// 78 | /// Trials 79 | /// 80 | [JsonProperty("TRIALS")] 81 | public FreeCompanyFocusEntryDefinition Trials { get; set; } 82 | 83 | /// 84 | /// Raids 85 | /// 86 | [JsonProperty("RAIDS")] 87 | public FreeCompanyFocusEntryDefinition Raids { get; set; } 88 | 89 | /// 90 | /// PvP 91 | /// 92 | [JsonProperty("PVP")] 93 | public FreeCompanyFocusEntryDefinition PvP { get; set; } 94 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/FreeCompany/FreeCompanyMembersEntryDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.FreeCompany; 4 | 5 | /// 6 | /// Definition for one FC member 7 | /// 8 | public class FreeCompanyMembersEntryDefinition : PagedEntryDefinition 9 | { 10 | /// 11 | /// Avatar of member character 12 | /// 13 | [JsonProperty("AVATAR")] 14 | public DefinitionsPack Avatar { get; set; } 15 | 16 | /// 17 | /// ID of character 18 | /// 19 | [JsonProperty("ID")] 20 | public DefinitionsPack Id { get; set; } 21 | 22 | /// 23 | /// Name of character 24 | /// 25 | [JsonProperty("NAME")] 26 | public DefinitionsPack Name { get; set; } 27 | 28 | /// 29 | /// Grand company rank 30 | /// 31 | [JsonProperty("RANK")] 32 | public DefinitionsPack Rank { get; set; } 33 | 34 | /// 35 | /// GC rank icon 36 | /// 37 | [JsonProperty("RANK_ICON")] 38 | public DefinitionsPack RankIcon { get; set; } 39 | 40 | /// 41 | /// Free company rank 42 | /// 43 | [JsonProperty("FC_RANK")] 44 | public DefinitionsPack FreeCompanyRank { get; set; } 45 | 46 | /// 47 | /// FC rank icon 48 | /// 49 | [JsonProperty("FC_RANK_ICON")] 50 | public DefinitionsPack FreeCompanyRankIcon { get; set; } 51 | 52 | /// 53 | /// Homeworld 54 | /// 55 | [JsonProperty("SERVER")] 56 | public DefinitionsPack Server { get; set; } 57 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/FreeCompany/FreeCompanyReputationDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.FreeCompany; 4 | 5 | /// 6 | /// Definition for reputation of FC with GC 7 | /// 8 | public class FreeCompanyReputationEntryDefinition : IDefinition 9 | { 10 | /// 11 | /// Name of the Grand Company 12 | /// 13 | [JsonProperty("NAME")] 14 | public DefinitionsPack Name { get; set; } 15 | 16 | /// 17 | /// Progress to next rank 18 | /// 19 | [JsonProperty("PROGRESS")] 20 | public DefinitionsPack Progress { get; set; } 21 | 22 | /// 23 | /// Current Rank 24 | /// 25 | [JsonProperty("RANK")] 26 | public DefinitionsPack Rank { get; set; } 27 | } 28 | 29 | /// 30 | /// Definition container for all reputations of a FC 31 | /// 32 | public class FreeCompanyReputationDefinition : IDefinition 33 | { 34 | /// 35 | /// Maelstrom 36 | /// 37 | [JsonProperty("MAELSTROM")] 38 | public FreeCompanyReputationEntryDefinition Maelstrom { get; set; } 39 | 40 | /// 41 | /// Order of the Twin Adder 42 | /// 43 | [JsonProperty("ADDERS")] 44 | public FreeCompanyReputationEntryDefinition Adders { get; set; } 45 | 46 | /// 47 | /// Immortal Flames 48 | /// 49 | [JsonProperty("FLAMES")] 50 | public FreeCompanyReputationEntryDefinition Flames { get; set; } 51 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/FreeCompany/FreeCompanySearchDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.FreeCompany; 4 | 5 | /// 6 | /// Definition container for one Free Company search result entry 7 | /// 8 | public class FreeCompanySearchEntryDefinition : PagedEntryDefinition 9 | { 10 | /// 11 | /// Crest layers 12 | /// 13 | [JsonProperty("CREST_LAYERS")] 14 | public IconLayersDefinition CrestLayers { get; set; } 15 | 16 | /// 17 | /// FC Id 18 | /// 19 | [JsonProperty("ID")] 20 | public DefinitionsPack Id { get; set; } 21 | 22 | /// 23 | /// Grand Company 24 | /// 25 | [JsonProperty("GRAND_COMPANY")] 26 | public DefinitionsPack GrandCompany { get; set; } 27 | 28 | /// 29 | /// Name 30 | /// 31 | [JsonProperty("NAME")] 32 | public DefinitionsPack Name { get; set; } 33 | 34 | /// 35 | /// World 36 | /// 37 | [JsonProperty("SERVER")] 38 | public DefinitionsPack Server { get; set; } 39 | 40 | /// 41 | /// FC active status 42 | /// 43 | [JsonProperty("ACTIVE")] 44 | public DefinitionsPack Active { get; set; } 45 | 46 | /// 47 | /// Active member count 48 | /// 49 | [JsonProperty("ACTIVE_MEMBERS")] 50 | public DefinitionsPack ActiveMembers { get; set; } 51 | 52 | /// 53 | /// Recruitment status 54 | /// 55 | [JsonProperty("RECRUITMENT_OPEN")] 56 | public DefinitionsPack RecruitmentOpen { get; set; } 57 | 58 | /// 59 | /// Estate state 60 | /// 61 | [JsonProperty("ESTATE_BUILT")] 62 | public DefinitionsPack EstateBuilt { get; set; } 63 | 64 | /// 65 | /// Formation date 66 | /// 67 | [JsonProperty("FORMED")] 68 | public DefinitionsPack Formed { get; set; } 69 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/IDefinition.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.Definitions.Model; 2 | 3 | /// 4 | /// Interface for all node definitions 5 | /// 6 | public interface IDefinition 7 | { 8 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/IconLayersDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model; 4 | 5 | /// 6 | /// Definition for an icon with multiple layers 7 | /// 8 | public class IconLayersDefinition : IDefinition 9 | { 10 | /// 11 | /// Bottom layer 12 | /// 13 | [JsonProperty("BOTTOM")] 14 | public DefinitionsPack Bottom { get; set; } 15 | 16 | /// 17 | /// Middle layer 18 | /// 19 | [JsonProperty("MIDDLE")] 20 | public DefinitionsPack Middle { get; set; } 21 | 22 | /// 23 | /// Top layer 24 | /// 25 | [JsonProperty("TOP")] 26 | public DefinitionsPack Top { get; set; } 27 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/Linkshell/LinkshellDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.Linkshell; 4 | 5 | /// 6 | /// Definitions for link shell 7 | /// 8 | public class LinkshellDefinition : IDefinition 9 | { 10 | /// 11 | /// Name 12 | /// 13 | [JsonProperty("NAME")] 14 | public DefinitionsPack Name { get; set; } 15 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/Linkshell/LinkshellMemberDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.Linkshell; 4 | 5 | /// 6 | /// Definition for one entry of the linkshell memebr list 7 | /// 8 | public class LinkshellMemberEntryDefinition : PagedEntryDefinition 9 | { 10 | /// 11 | /// Avatar 12 | /// 13 | [JsonProperty("AVATAR")] public DefinitionsPack Avatar { get; set; } 14 | 15 | /// 16 | /// ID 17 | /// 18 | [JsonProperty("ID")] public DefinitionsPack Id { get; set; } 19 | 20 | /// 21 | /// Name 22 | /// 23 | [JsonProperty("NAME")] public DefinitionsPack Name { get; set; } 24 | 25 | /// 26 | /// Rank 27 | /// 28 | [JsonProperty("RANK")] public DefinitionsPack Rank { get; set; } 29 | 30 | /// 31 | /// Rank Icon 32 | /// 33 | [JsonProperty("RANK_ICON")] public DefinitionsPack RankIcon { get; set; } 34 | 35 | /// 36 | /// Linkshell rank 37 | /// 38 | [JsonProperty("LINKSHELL_RANK")] public DefinitionsPack LinkshellRank { get; set; } 39 | 40 | /// 41 | /// Linkshell rank Icon 42 | /// 43 | [JsonProperty("LINKSHELL_RANK_ICON")] public DefinitionsPack LinkshellRankIcon { get; set; } 44 | 45 | /// 46 | /// Server 47 | /// 48 | [JsonProperty("SERVER")] public DefinitionsPack Server { get; set; } 49 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/Linkshell/LinkshellSearchEntryDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model.Linkshell; 4 | 5 | /// 6 | /// Definition container for one Link-Shell search result entry 7 | /// 8 | public class LinkshellSearchEntryDefinition : PagedEntryDefinition 9 | { 10 | /// 11 | /// ID 12 | /// 13 | [JsonProperty("ID")] public DefinitionsPack Id { get; set; } 14 | 15 | /// 16 | /// Name 17 | /// 18 | [JsonProperty("NAME")] public DefinitionsPack Name { get; set; } 19 | 20 | /// 21 | /// Rank 22 | /// 23 | [JsonProperty("SERVER")] public DefinitionsPack Server { get; set; } 24 | 25 | /// 26 | /// Rank Icon 27 | /// 28 | [JsonProperty("ACTIVE_MEMBERS")] public DefinitionsPack ActiveMembers { get; set; } 29 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/MetaDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace NetStone.Definitions.Model; 4 | 5 | /// 6 | /// Hold information about the uri for which the definition packs are valid 7 | /// 8 | public class ApplicableUris 9 | { 10 | /// 11 | /// Uri that holds information about FC focus 12 | /// 13 | [JsonProperty("freecompany/focus.json")] 14 | public string? FreecompanyFocusJson { get; set; } 15 | 16 | /// 17 | /// Uri that holds information about FC 18 | /// 19 | [JsonProperty("freecompany/freecompany.json")] 20 | public string? FreecompanyFreecompanyJson { get; set; } 21 | 22 | /// 23 | /// Uri that holds information about FC members 24 | /// 25 | [JsonProperty("freecompany/members.json")] 26 | public string? FreecompanyMembersJson { get; set; } 27 | 28 | /// 29 | /// Uri that holds information about FC reputation 30 | /// 31 | [JsonProperty("freecompany/reputation.json")] 32 | public string? FreecompanyReputationJson { get; set; } 33 | 34 | /// 35 | /// Uri that holds information about FC recruitment 36 | /// 37 | [JsonProperty("freecompany/seeking.json")] 38 | public string? FreecompanySeekingJson { get; set; } 39 | 40 | /// 41 | /// Uri that holds information about Cross World Link Shells 42 | /// 43 | [JsonProperty("cwls/cwls.json")] 44 | public string? LinkshellCrossworldCwlsJson { get; set; } 45 | 46 | /// 47 | /// Uri that holds information about Cross World Link Shell members 48 | /// 49 | [JsonProperty("cwls/members.json")] 50 | public string? LinkshellCrossworldMembersJson { get; set; } 51 | 52 | 53 | /// 54 | /// Uri that holds information about Link Shells 55 | /// 56 | [JsonProperty("linkshell/ls.json")] 57 | public string? LinkshellLsJson { get; set; } 58 | 59 | 60 | /// 61 | /// Uri that holds information about Link Shell members 62 | /// 63 | [JsonProperty("linkshell/members.json")] 64 | public string? LinkshellMembersJson { get; set; } 65 | 66 | 67 | /// 68 | /// Uri that holds information about a character's achievements 69 | /// 70 | [JsonProperty("profile/achievements.json")] 71 | public string? ProfileAchievementsJson { get; set; } 72 | 73 | /// 74 | /// Uri that holds information about a character's attributes 75 | /// 76 | [JsonProperty("profile/attributes.json")] 77 | public string? ProfileAttributesJson { get; set; } 78 | 79 | /// 80 | /// Uri that holds information about a character 81 | /// 82 | [JsonProperty("profile/character.json")] 83 | public string? ProfileCharacterJson { get; set; } 84 | 85 | /// 86 | /// Uri that holds information about a character jobs/classes 87 | /// 88 | [JsonProperty("profile/classjob.json")] 89 | public string? ProfileClassjobJson { get; set; } 90 | 91 | /// 92 | /// Uri that holds information about a character's gear 93 | /// 94 | [JsonProperty("profile/gearset.json")] 95 | public string? ProfileGearsetJson { get; set; } 96 | 97 | /// 98 | /// Uri that holds information about a character's minions 99 | /// 100 | [JsonProperty("profile/minion.json")] 101 | public string? ProfileMinionJson { get; set; } 102 | 103 | /// 104 | /// Uri that holds information about a character's mounts 105 | /// 106 | [JsonProperty("profile/mount.json")] 107 | public string? ProfileMountJson { get; set; } 108 | 109 | /// 110 | /// Uri that holds information about a PVP team's members 111 | /// 112 | [JsonProperty("pvpteam/members.json")] 113 | public string? PvpteamMembersJson { get; set; } 114 | 115 | /// 116 | /// Uri that holds information about a PVP team 117 | /// 118 | [JsonProperty("pvpteam/pvpteam.json")] 119 | public string? PvpteamPvpteamJson { get; set; } 120 | 121 | /// 122 | /// Uri that let's you search characters 123 | /// 124 | [JsonProperty("search/character.json")] 125 | public string? SearchCharacterJson { get; set; } 126 | 127 | /// 128 | /// Uri that let's you search Cross World Link Shells 129 | /// 130 | [JsonProperty("search/cwls.json")] 131 | public string? SearchCwlsJson { get; set; } 132 | 133 | /// 134 | /// Uri that let's you search Free Companies 135 | /// 136 | [JsonProperty("search/freecompany.json")] 137 | public string? SearchFreecompanyJson { get; set; } 138 | 139 | /// 140 | /// Uri that let's you search Linkshells 141 | /// 142 | [JsonProperty("search/linkshell.json")] 143 | public string? SearchLinkshellJson { get; set; } 144 | 145 | /// 146 | /// Uri that let's you search PvP teams 147 | /// 148 | [JsonProperty("search/pvpteam.json")] 149 | public string? SearchPvpteamJson { get; set; } 150 | } 151 | 152 | /// 153 | /// Holds meta information regarding the definitions 154 | /// 155 | public class MetaDefinition : IDefinition 156 | { 157 | /// 158 | /// Version 159 | /// 160 | [JsonProperty("version")] 161 | public string Version { get; set; } 162 | 163 | /// 164 | /// User Agent used to get desktop version of Lodestone 165 | /// 166 | [JsonProperty("userAgentDesktop")] 167 | public string UserAgentDesktop { get; set; } 168 | 169 | /// 170 | /// User Agent used to get mobile version of Lodestone 171 | /// 172 | [JsonProperty("userAgentMobile")] 173 | public string UserAgentMobile { get; set; } 174 | 175 | /// 176 | /// Collection of Uris for which the definition packs are valid 177 | /// 178 | [JsonProperty("applicableUris")] 179 | public ApplicableUris ApplicableUris { get; set; } 180 | } -------------------------------------------------------------------------------- /NetStone/Definitions/Model/PagedDefinition.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace NetStone.Definitions.Model; 5 | 6 | /// 7 | /// Base definition for paged results 8 | /// 9 | public class PagedDefinition : IDefinition where TEntry : PagedEntryDefinition 10 | { 11 | /// 12 | /// Root node 13 | /// 14 | [JsonProperty("ROOT")] 15 | public DefinitionsPack Root { get; set; } 16 | 17 | /// 18 | /// Definition for one entry 19 | /// 20 | [JsonProperty("ENTRY")] 21 | public TEntry Entry { get; set; } 22 | 23 | /// 24 | /// Info about pages 25 | /// 26 | [JsonProperty("PAGE_INFO")] 27 | public DefinitionsPack PageInfo { get; set; } 28 | 29 | /// 30 | /// Button for next page 31 | /// 32 | [JsonProperty("LIST_NEXT_BUTTON")] 33 | public DefinitionsPack ListNextButton { get; set; } 34 | 35 | /// 36 | /// DEfinition for node for empty results 37 | /// 38 | [JsonProperty("NO_RESULTS_FOUND")] 39 | public DefinitionsPack? NoResultsFound { get; set; } 40 | } 41 | 42 | /// 43 | /// Base definition of a paged entry 44 | /// 45 | public class PagedEntryDefinition 46 | { 47 | /// 48 | /// Root node of entry 49 | /// 50 | [JsonProperty("ROOT")] 51 | public DefinitionsPack Root { get; set; } 52 | } -------------------------------------------------------------------------------- /NetStone/Definitions/XivApiDefinitionsContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Threading.Tasks; 4 | using NetStone.Definitions.Model; 5 | using NetStone.Definitions.Model.Character; 6 | using NetStone.Definitions.Model.CWLS; 7 | using NetStone.Definitions.Model.FreeCompany; 8 | using NetStone.Definitions.Model.Linkshell; 9 | using Newtonsoft.Json; 10 | 11 | namespace NetStone.Definitions; 12 | 13 | /// 14 | /// Holds the definitions on how to find and parse values from Lodestone HTML 15 | /// 16 | public class XivApiDefinitionsContainer : DefinitionsContainer 17 | { 18 | private const string DefinitionRepoBase = "https://raw.githubusercontent.com/xivapi/lodestone-css-selectors/main/"; 19 | 20 | private readonly HttpClient client; 21 | 22 | /// 23 | /// Constructs this class without populating definitions 24 | /// 25 | public XivApiDefinitionsContainer() 26 | { 27 | this.client = new HttpClient 28 | { 29 | BaseAddress = new Uri(DefinitionRepoBase), 30 | }; 31 | } 32 | 33 | /// 34 | /// Fetches current CSS selector definitions from xivapi/lodestone-css-selectors github repository. 35 | /// 36 | /// 37 | /// 38 | /// Task for this operation 39 | public override async Task Reload() 40 | { 41 | this.Meta = await GetDefinition("meta.json"); 42 | 43 | this.Character = await GetDefinition("profile/character.json"); 44 | this.ClassJob = await GetDefinition("profile/classjob.json"); 45 | this.Gear = await GetDefinition("profile/gearset.json"); 46 | this.Attributes = await GetDefinition("profile/attributes.json"); 47 | this.Achievement = await GetDefinition("profile/achievements.json"); 48 | this.Mount = await GetDefinition("profile/mount.json"); 49 | this.Minion = await GetDefinition("profile/minion.json"); 50 | 51 | this.FreeCompany = await GetDefinition("freecompany/freecompany.json"); 52 | this.FreeCompanyFocus = await GetDefinition("freecompany/focus.json"); 53 | this.FreeCompanyReputation = 54 | await GetDefinition("freecompany/reputation.json"); 55 | 56 | this.FreeCompanyMembers = await GetDefinition>("freecompany/members.json"); 57 | 58 | this.CharacterSearch = await GetDefinition>("search/character.json"); 59 | this.FreeCompanySearch = await GetDefinition>("search/freecompany.json"); 60 | 61 | this.CrossworldLinkshell = await GetDefinition("cwls/cwls.json"); 62 | this.CrossworldLinkshellMember = await GetDefinition>("cwls/members.json"); 63 | this.CrossworldLinkshellSearch = await GetDefinition>("search/cwls.json"); 64 | 65 | this.Linkshell = await GetDefinition("linkshell/ls.json"); 66 | this.LinkshellMember = await GetDefinition>("linkshell/members.json"); 67 | this.LinkshellSearch = await GetDefinition>("search/linkshell.json"); 68 | } 69 | 70 | private async Task GetDefinition(string path) where T : IDefinition 71 | { 72 | var json = await this.client.GetStringAsync(path); 73 | var result = JsonConvert.DeserializeObject(json); 74 | return result == null ? throw new FormatException($"Could not parse definitions in {path}.") : result; 75 | } 76 | 77 | /// 78 | public override void Dispose() 79 | { 80 | this.client.Dispose(); 81 | } 82 | } -------------------------------------------------------------------------------- /NetStone/GameData/GameDataInfo.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.GameData; 2 | 3 | /// 4 | /// Generic container for game data 5 | /// 6 | public struct GameDataInfo 7 | { 8 | /// 9 | /// Key /ID of data 10 | /// 11 | public uint Key { get; set; } 12 | 13 | /// 14 | /// Name 15 | /// 16 | public string Name { get; set; } 17 | } -------------------------------------------------------------------------------- /NetStone/GameData/GenderedGameData.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.GameData; 2 | 3 | /// 4 | /// Game data with different names depending on gender 5 | /// 6 | public struct GenderedGameData 7 | { 8 | /// 9 | /// Basic game data 10 | /// 11 | public GameDataInfo Info { get; set; } 12 | 13 | /// 14 | /// Masculine name 15 | /// 16 | public LanguageStrings NameMasc { get; set; } 17 | 18 | /// 19 | /// Feminine name 20 | /// 21 | public LanguageStrings NameFem { get; set; } 22 | } -------------------------------------------------------------------------------- /NetStone/GameData/IGameDataProvider.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.GameData; 2 | 3 | /// 4 | /// Needed interface for a provider of game data 5 | /// 6 | public interface IGameDataProvider 7 | { 8 | /// 9 | /// Get item data by name. 10 | /// 11 | /// Name of the item 12 | /// Game data 13 | public NamedGameData? GetItem(string name); 14 | } -------------------------------------------------------------------------------- /NetStone/GameData/LanguageStrings.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.GameData; 2 | 3 | /// 4 | /// Container for localized strings 5 | /// 6 | public struct LanguageStrings 7 | { 8 | /// 9 | /// English 10 | /// 11 | public string En { get; set; } 12 | 13 | /// 14 | /// German 15 | /// 16 | public string De { get; set; } 17 | 18 | /// 19 | /// Japanese 20 | /// 21 | public string Ja { get; set; } 22 | 23 | /// 24 | /// French 25 | /// 26 | public string Fr { get; set; } 27 | } -------------------------------------------------------------------------------- /NetStone/GameData/NamedGameData.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.GameData; 2 | 3 | /// 4 | /// Game data with localized name 5 | /// 6 | public struct NamedGameData 7 | { 8 | /// 9 | /// Base data 10 | /// 11 | public GameDataInfo Info { get; set; } 12 | 13 | /// 14 | /// Localized name 15 | /// 16 | public LanguageStrings Name { get; set; } 17 | } -------------------------------------------------------------------------------- /NetStone/GameData/TitleGameData.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.GameData; 2 | 3 | /// 4 | /// Game data for titles 5 | /// 6 | public struct TitleGameData 7 | { 8 | /// 9 | /// Localized title 10 | /// 11 | public NamedGameData Names { get; set; } 12 | 13 | /// 14 | /// Indicates if before name 15 | /// 16 | public bool Prefix { get; set; } 17 | } -------------------------------------------------------------------------------- /NetStone/Model/IOptionalParseable.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.Model; 2 | 3 | /// 4 | /// Interface indicating an optional parsed object that may or may not exist. 5 | /// 6 | /// LodestoneParseable to be marked optional 7 | public interface IOptionalParseable where T : LodestoneParseable 8 | { 9 | /// 10 | /// Value indicating if the object should be populated. 11 | /// 12 | bool Exists { get; } 13 | } 14 | 15 | /// 16 | /// 17 | /// 18 | public static class OptionalExtensions 19 | { 20 | /// 21 | /// Method returning the object itself if exists. 22 | /// 23 | /// this if Exists == True 24 | public static T? GetOptional(this IOptionalParseable opt) where T : LodestoneParseable 25 | { 26 | if (opt.Exists) 27 | return opt as T; 28 | 29 | return null; 30 | } 31 | } -------------------------------------------------------------------------------- /NetStone/Model/IPaginatedResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using HtmlAgilityPack; 5 | using NetStone.Definitions.Model; 6 | using NetStone.Search; 7 | 8 | namespace NetStone.Model; 9 | 10 | /// 11 | /// Models data that is presented over multiple pages 12 | /// 13 | /// Type of data presented 14 | public interface IPaginatedResult where T : LodestoneParseable 15 | { 16 | /// 17 | /// Currently handled page 18 | /// 19 | int CurrentPage { get; } 20 | 21 | /// 22 | /// Total number of pages 23 | /// 24 | int NumPages { get; } 25 | 26 | /// 27 | /// Gets the next page of results 28 | /// 29 | /// Task of retrieving next page 30 | Task GetNextPage(); 31 | } 32 | 33 | /// 34 | /// Container class holding paginated information 35 | /// 36 | public abstract class PaginatedIdResult 37 | : PaginatedResult where TPage : LodestoneParseable 38 | where TEntry : LodestoneParseable 39 | where TEntryDef : PagedEntryDefinition 40 | { 41 | /// 42 | protected PaginatedIdResult(HtmlNode rootNode, PagedDefinition pageDefinition, 43 | Func> nextPageFunc, string id) 44 | : base(rootNode, pageDefinition, nextPageFunc, id) 45 | { 46 | } 47 | } 48 | /// 49 | /// Container class holding paginated information 50 | /// 51 | public abstract class PaginatedSearchResult 52 | : PaginatedResult where TPage : LodestoneParseable 53 | where TEntry : LodestoneParseable 54 | where TEntryDef : PagedEntryDefinition 55 | where TQuery : ISearchQuery 56 | { 57 | /// 58 | protected PaginatedSearchResult(HtmlNode rootNode, PagedDefinition pageDefinition, 59 | Func> nextPageFunc, 60 | TQuery query) 61 | : base(rootNode, pageDefinition, nextPageFunc, query) 62 | { 63 | } 64 | } 65 | 66 | 67 | /// 68 | /// Container class holding paginated information 69 | /// 70 | public abstract class PaginatedResult : LodestoneParseable, IPaginatedResult where TPage : LodestoneParseable where TEntry : LodestoneParseable where TEntryDef : PagedEntryDefinition 71 | { 72 | /// 73 | /// Definition for the paginated type 74 | /// 75 | protected readonly PagedDefinition PageDefinition; 76 | 77 | private readonly TRequest request; 78 | 79 | private readonly Func> nextPageFunc; 80 | 81 | /// 82 | /// 83 | /// 84 | /// The root document node of the page 85 | /// CSS definitions for the paginated type 86 | /// Function to retrieve a page of this type 87 | /// The input used to request further pages. 88 | protected PaginatedResult(HtmlNode rootNode, PagedDefinition pageDefinition,Func> nextPageFunc, TRequest request) : base(rootNode) 89 | { 90 | this.PageDefinition = pageDefinition; 91 | this.request = request; 92 | this.nextPageFunc = nextPageFunc; 93 | } 94 | 95 | /// 96 | /// If there is any data 97 | /// 98 | public bool HasResults => this.PageDefinition.NoResultsFound is null || !HasNode(this.PageDefinition.NoResultsFound); 99 | 100 | private TEntry[]? parsedResults; 101 | 102 | /// 103 | /// List of members 104 | /// 105 | protected IEnumerable Results 106 | { 107 | get 108 | { 109 | if (!this.HasResults) return Array.Empty(); 110 | this.parsedResults ??= ParseResults(); 111 | return this.parsedResults; 112 | } 113 | } 114 | 115 | /// 116 | /// Creates the array of all entries on this page> 117 | /// 118 | protected abstract TEntry[] ParseResults(); 119 | 120 | private int? currentPageVal; 121 | 122 | /// 123 | public int CurrentPage 124 | { 125 | get 126 | { 127 | if (!this.HasResults) 128 | return 0; 129 | if (!this.currentPageVal.HasValue) 130 | ParsePagesCount(); 131 | 132 | return this.currentPageVal!.Value; 133 | } 134 | } 135 | 136 | private int? numPagesVal; 137 | 138 | /// 139 | public int NumPages 140 | { 141 | get 142 | { 143 | if (!this.HasResults) 144 | return 0; 145 | if (!this.numPagesVal.HasValue) 146 | ParsePagesCount(); 147 | 148 | return this.numPagesVal!.Value; 149 | } 150 | } 151 | private void ParsePagesCount() 152 | { 153 | var results = ParseRegex(this.PageDefinition.PageInfo); 154 | 155 | this.currentPageVal = int.Parse(results["CurrentPage"].Value); 156 | this.numPagesVal = int.Parse(results["NumPages"].Value); 157 | } 158 | 159 | /// 160 | public async Task GetNextPage() 161 | { 162 | if (!this.HasResults) 163 | return null; 164 | 165 | if (this.CurrentPage == this.NumPages) 166 | return null; 167 | 168 | return await this.nextPageFunc(this.request, this.CurrentPage + 1); 169 | } 170 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/CWLS/LodestoneCrossworldLinkshell.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions; 4 | using NetStone.Definitions.Model.CWLS; 5 | using NetStone.Model.Parseables.CWLS.Members; 6 | 7 | namespace NetStone.Model.Parseables.CWLS; 8 | 9 | /// 10 | /// Container class holding information about a cross world linkshell and it's members. 11 | /// 12 | public class LodestoneCrossworldLinkshell : PaginatedIdResult 13 | { 14 | 15 | private readonly CrossworldLinkshellDefinition definition; 16 | 17 | /// 18 | /// Container class for a parseable corss world linkshell page. 19 | /// 20 | /// The to be used to fetch further information. 21 | /// The root document node of the page. 22 | /// The holding definitions to be used to access data. 23 | /// The ID of the cross world linkshell. 24 | public LodestoneCrossworldLinkshell(LodestoneClient client, HtmlNode rootNode, DefinitionsContainer container, string id) 25 | : base(rootNode,container.CrossworldLinkshellMember,client.GetCrossworldLinkshell,id) 26 | { 27 | this.definition = container.CrossworldLinkshell; 28 | } 29 | 30 | /// 31 | /// Name 32 | /// 33 | public string Name => ParseDirectInnerText(this.definition.Name).Trim(); 34 | 35 | /// 36 | /// Datacenter 37 | /// 38 | public string DataCenter => Parse(this.definition.DataCenter); 39 | 40 | /// 41 | /// Members 42 | /// 43 | public IEnumerable Members => this.Results; 44 | 45 | /// 46 | protected override CrossworldLinkshellMemberEntry[] ParseResults() 47 | { 48 | var nodes = QueryContainer(this.PageDefinition); 49 | 50 | var parsedResults = new CrossworldLinkshellMemberEntry[nodes.Length]; 51 | for (var i = 0; i < parsedResults.Length; i++) 52 | { 53 | parsedResults[i] = new CrossworldLinkshellMemberEntry(nodes[i], this.PageDefinition.Entry); 54 | } 55 | return parsedResults; 56 | } 57 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/CWLS/Members/CrossworldLinkshellMemberEntry.cs: -------------------------------------------------------------------------------- 1 | using HtmlAgilityPack; 2 | using NetStone.Definitions.Model.CWLS; 3 | 4 | namespace NetStone.Model.Parseables.CWLS.Members; 5 | 6 | /// 7 | /// Container class holding information about a cross-world linkshell member. 8 | /// 9 | public class CrossworldLinkshellMemberEntry : LodestoneParseable 10 | { 11 | private readonly CrossworldLinkshellMemberEntryDefinition definition; 12 | /// 13 | /// Create instance of member entry for a given node 14 | /// 15 | /// Root html node of this entry 16 | /// Css and regex definition 17 | public CrossworldLinkshellMemberEntry(HtmlNode rootNode, CrossworldLinkshellMemberEntryDefinition definition) : base(rootNode) 18 | { 19 | this.definition = definition; 20 | } 21 | 22 | /// 23 | /// Avatar 24 | /// 25 | public string Avatar => Parse(this.definition.Avatar); 26 | 27 | /// 28 | /// ID 29 | /// 30 | public string? Id => ParseHrefId(this.definition.Id); 31 | 32 | /// 33 | /// Name 34 | /// 35 | public string Name => Parse(this.definition.Name); 36 | 37 | /// 38 | /// Rank 39 | /// 40 | public string Rank => Parse(this.definition.Rank); 41 | 42 | /// 43 | /// Rank Icon 44 | /// 45 | public string RankIcon => Parse(this.definition.RankIcon); 46 | 47 | /// 48 | /// Linkshell rank 49 | /// 50 | public string LinkshellRank => Parse(this.definition.LinkshellRank); 51 | 52 | /// 53 | /// Linkshell rank Icon 54 | /// 55 | public string LinkshellRankIcon => Parse(this.definition.LinkshellRankIcon); 56 | 57 | /// 58 | /// Server 59 | /// 60 | public string Server => Parse(this.definition.Server); 61 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Character/Achievement/CharacterAchievementEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using HtmlAgilityPack; 4 | using NetStone.Definitions.Model.Character; 5 | 6 | namespace NetStone.Model.Parseables.Character.Achievement; 7 | 8 | /// 9 | /// Models data for one Achievement this character earned 10 | /// 11 | public class CharacterAchievementEntry : LodestoneParseable 12 | { 13 | private readonly CharacterAchievementEntryDefinition definition; 14 | 15 | /// 16 | /// Create instance of achievement entry fpr given node 17 | /// 18 | /// Root html node of this entry 19 | /// Css and regex definition 20 | public CharacterAchievementEntry(HtmlNode rootNode, CharacterAchievementEntryDefinition definition) : base(rootNode) 21 | { 22 | this.definition = definition; 23 | } 24 | 25 | /// 26 | /// The Name of this achievement 27 | /// 28 | #if NETSTANDARD2_1 29 | public string Name => ParseRegex(this.definition.Name).First(r => r.Name.Equals("Name")).Value; 30 | #else 31 | public string Name => ParseRegex(this.definition.Name).Values.First(r => r.Name.Equals("Name")).Value; 32 | #endif 33 | /// 34 | /// ID of this achievement 35 | /// 36 | public ulong? Id => ParseHrefIdULong(this.definition.Id); 37 | 38 | /// 39 | /// Link to the Eorzean Database 40 | /// 41 | public Uri? DatabaseLink => ParseHref(this.definition.Id); 42 | 43 | /// 44 | /// Time when this character earned this achievement 45 | /// 46 | public DateTime TimeAchieved => ParseTime(this.definition.Time); 47 | 48 | /// 49 | public override string ToString() => this.Name; 50 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Character/Achievement/CharacterAchievementPage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model.Character; 4 | 5 | namespace NetStone.Model.Parseables.Character.Achievement; 6 | 7 | /// 8 | /// Holds information about a characters unlocked achievements 9 | /// 10 | public class CharacterAchievementPage : PaginatedIdResult 11 | { 12 | private readonly CharacterAchievementDefinition definition; 13 | 14 | /// 15 | /// Creates a new instance retrieving information about a characters unlocked achievements 16 | /// 17 | /// Lodestone client instance 18 | /// Root node of the achievement page 19 | /// Parse definition pack 20 | /// ID of the character 21 | public CharacterAchievementPage(LodestoneClient client, HtmlNode rootNode, 22 | CharacterAchievementDefinition definition,string charId) 23 | : base(rootNode, definition, client.GetCharacterAchievement, charId) 24 | { 25 | this.definition = definition; 26 | } 27 | 28 | /// 29 | /// Total number of achievements 30 | /// 31 | public int TotalAchievements 32 | { 33 | get 34 | { 35 | var res = ParseRegex(this.definition.TotalAchievements); 36 | return int.Parse(res["TotalAchievements"].Value); 37 | } 38 | } 39 | 40 | /// 41 | /// Number of achievement points for this character 42 | /// 43 | public int AchievementPoints => int.Parse(Parse(this.definition.AchievementPoints)); 44 | 45 | /// 46 | /// Unlocked achievements for character 47 | /// 48 | public IEnumerable Achievements => this.Results; 49 | 50 | /// 51 | protected override CharacterAchievementEntry[] ParseResults() 52 | { 53 | var nodes = QueryContainer(this.definition); 54 | 55 | var parsedResults = new CharacterAchievementEntry[nodes.Length]; 56 | for (var i = 0; i < parsedResults.Length; i++) 57 | { 58 | parsedResults[i] = new CharacterAchievementEntry(nodes[i], this.definition.Entry); 59 | } 60 | return parsedResults; 61 | } 62 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Character/CharacterAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model.Character; 4 | 5 | namespace NetStone.Model.Parseables.Character; 6 | 7 | /// 8 | /// Provides information about a characters attributes 9 | /// 10 | public class CharacterAttributes : LodestoneParseable 11 | { 12 | private readonly CharacterAttributesDefinition definition; 13 | 14 | /// 15 | /// Creates an instance that provides Attributes for given node 16 | /// 17 | /// Root HTML node of the character profile page on Lodestone 18 | /// Definitions on how to parse attributes from the HTML 19 | public CharacterAttributes(HtmlNode rootNode, CharacterAttributesDefinition definition) : base(rootNode) 20 | { 21 | this.definition = definition; 22 | } 23 | 24 | /// 25 | /// This characters' Strength value. 26 | /// 27 | public int Strength => int.Parse(Parse(this.definition.Strength)); 28 | 29 | /// 30 | /// This characters' Dexterity value. 31 | /// 32 | public int Dexterity => int.Parse(Parse(this.definition.Dexterity)); 33 | 34 | /// 35 | /// This characters' Vitality value. 36 | /// 37 | public int Vitality => int.Parse(Parse(this.definition.Vitality)); 38 | 39 | /// 40 | /// This characters' Intelligence value. 41 | /// 42 | public int Intelligence => int.Parse(Parse(this.definition.Intelligence)); 43 | 44 | /// 45 | /// This characters' Mind value. 46 | /// 47 | public int Mind => int.Parse(Parse(this.definition.Mind)); 48 | 49 | /// 50 | /// This characters' Critical Hit Rate value. 51 | /// 52 | public int CriticalHitRate => int.Parse(Parse(this.definition.CriticalHitRate)); 53 | 54 | /// 55 | /// This characters' Determination value. 56 | /// 57 | public int Determination => int.Parse(Parse(this.definition.Determination)); 58 | 59 | /// 60 | /// This characters' Direct Hit Rate value. 61 | /// 62 | public int DirectHitRate => int.Parse(Parse(this.definition.DirectHitRate)); 63 | 64 | /// 65 | /// This characters' Defense value. 66 | /// 67 | public int Defense => int.Parse(Parse(this.definition.Defense)); 68 | 69 | /// 70 | /// This characters' Magic Defense value. 71 | /// 72 | public int MagicDefense => int.Parse(Parse(this.definition.MagicDefense)); 73 | 74 | /// 75 | /// This characters' Attack Power value. 76 | /// 77 | public int AttackPower => int.Parse(Parse(this.definition.AttackPower)); 78 | 79 | /// 80 | /// This characters' Skill Speed value. 81 | /// 82 | public int SkillSpeed => int.Parse(Parse(this.definition.SkillSpeed)); 83 | 84 | /// 85 | /// This characters' Attack Magic Potency value. 86 | /// 87 | /// This value is only set for disciples of war/magic. 88 | public int? AttackMagicPotency => IsDoWOrDoM() ? this.AttackMagicPotencyInternal : null; 89 | 90 | /// 91 | /// This characters' Healing Magic Potency value. 92 | /// 93 | /// This value is only set for disciples of war/magic. 94 | public int? HealingMagicPotency => IsDoWOrDoM() ? this.HealingMagicPotencyInternal : null; 95 | 96 | /// 97 | /// This characters' Spell Speed value. 98 | /// 99 | /// This value is only set for disciples of war/magic. 100 | public int? SpellSpeed => int.TryParse(Parse(this.definition.SpellSpeed), out var result) ? result : null; 101 | 102 | /// 103 | /// This characters' Tenacity value. 104 | /// 105 | public int? Tenacity => int.TryParse(Parse(this.definition.Tenacity), out var result) ? result : null; 106 | 107 | /// 108 | /// This characters' Piety value. 109 | /// 110 | public int? Piety => int.TryParse(Parse(this.definition.Piety), out var result) ? result : null; 111 | 112 | /// 113 | /// This characters' Craftmanship value. 114 | /// 115 | /// This value is only set for disciples of the hand. 116 | public int? Craftsmanship => IsDoH() ? this.AttackMagicPotencyInternal : null; 117 | 118 | /// 119 | /// This characters' Control value. 120 | /// 121 | /// This value is only set for disciples of the hand. 122 | public int? Control => IsDoH() ? this.HealingMagicPotencyInternal : null; 123 | 124 | /// 125 | /// This characters' Gathering value. 126 | /// 127 | /// This value is only set for disciples of the land. 128 | public int? Gathering => IsDoL() ? this.AttackMagicPotencyInternal : null; 129 | 130 | /// 131 | /// This characters' Perception value. 132 | /// 133 | /// This value is only set for disciples of the land. 134 | public int? Perception => IsDoL() ? this.HealingMagicPotencyInternal : null; 135 | 136 | /// 137 | /// This characters' HP value. 138 | /// 139 | public int Hp => int.Parse(Parse(this.definition.Hp)); 140 | 141 | /// 142 | /// This characters' MP, GP or CP value. Check the Property to find out which. 143 | /// 144 | public int MpGpCp => int.Parse(Parse(this.definition.MpGpCp)); 145 | 146 | /// 147 | /// Value indicating which of MP, GP, or CP is indicated by . 148 | /// 149 | public string MpGpCpParameterName => Parse(this.definition.MpGpCpParameterName); 150 | 151 | internal bool IsDoL() => this.MpGpCpParameterName.Equals("GP", StringComparison.InvariantCultureIgnoreCase); 152 | internal bool IsDoWOrDoM() => this.MpGpCpParameterName.Equals("MP", StringComparison.InvariantCultureIgnoreCase); 153 | internal bool IsDoH() => this.MpGpCpParameterName.Equals("CP", StringComparison.InvariantCultureIgnoreCase); 154 | 155 | internal int AttackMagicPotencyInternal => int.TryParse(Parse(this.definition.AttackMagicPotency), out var val) ? val : 0; 156 | 157 | internal int HealingMagicPotencyInternal => int.TryParse(Parse(this.definition.HealingMagicPotency), out var val) ? val : 0; 158 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Character/ClassJob/ClassJobBozja.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using HtmlAgilityPack; 4 | using NetStone.Definitions.Model.Character; 5 | 6 | namespace NetStone.Model.Parseables.Character.ClassJob; 7 | 8 | /// 9 | /// Entry for Bozja Field Operation of a character 10 | /// 11 | public class ClassJobBozja : LodestoneParseable, IOptionalParseable 12 | { 13 | private readonly ClassJobBozjaDefinition definition; 14 | 15 | /// 16 | /// Constructs a new class entry 17 | /// 18 | /// Root node of this entry 19 | /// Parser definition 20 | public ClassJobBozja(HtmlNode rootNode, ClassJobBozjaDefinition definition) : base(rootNode) 21 | { 22 | this.definition = definition; 23 | } 24 | 25 | /// 26 | /// The name of this class and job combo. 27 | /// 28 | public string Name => Parse(this.definition.NAME); 29 | 30 | /// 31 | /// The level this class or job is at. 32 | /// 33 | public int Level => int.TryParse(Parse(this.definition.LEVEL), out var levelOut) ? levelOut : 0 ; 34 | 35 | private string MettleString => ParseInnerText(this.definition.METTLE); 36 | 37 | private int? mettleCurrentVal; 38 | 39 | /// 40 | /// The amount of current achieved EXP on this level. 41 | /// 42 | public int MettleCurrent 43 | { 44 | get 45 | { 46 | if (!this.mettleCurrentVal.HasValue) 47 | ParseMettle(); 48 | 49 | return this.mettleCurrentVal!.Value; 50 | } 51 | } 52 | 53 | private int? mettleMaxVal; 54 | 55 | /// 56 | /// The amount of EXP to be reached to gain the next level. 57 | /// 58 | public int MettleMax 59 | { 60 | get 61 | { 62 | if (!this.mettleCurrentVal.HasValue) 63 | ParseMettle(); 64 | 65 | return this.mettleMaxVal!.Value; 66 | } 67 | } 68 | 69 | /// 70 | /// The outstanding amount of EXP to go to the next level. 71 | /// 72 | public int MettleToGo => this.MettleMax - this.MettleCurrent; 73 | 74 | private void ParseMettle() 75 | { 76 | if (!this.Exists) 77 | { 78 | this.mettleCurrentVal = 0; 79 | this.mettleMaxVal = 0; 80 | 81 | return; 82 | } 83 | 84 | var mettleVals = this.MettleString.Split(" / ").Select(x => x.Replace(",", string.Empty)).ToArray(); 85 | 86 | this.mettleCurrentVal = int.TryParse(Regex.Match(mettleVals[0], @"\d+").Value, out var mettleCur) ? mettleCur : 0; 87 | this.mettleMaxVal = int.TryParse(Regex.Match(mettleVals[1], @"\d+").Value, out var mettleMax) ? mettleMax : 0; 88 | } 89 | 90 | /// 91 | /// Value indicating if this class is unlocked. 92 | /// 93 | public bool Exists => this.Level != 0; 94 | 95 | /// 96 | /// The string representation of this object. 97 | /// 98 | /// Name (Level) 99 | public override string ToString() => $"{this.Name} ({this.Level})"; 100 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Character/ClassJob/ClassJobEntry.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model.Character; 4 | 5 | namespace NetStone.Model.Parseables.Character.ClassJob; 6 | 7 | /// 8 | /// Entry for one class/job of a character 9 | /// 10 | public class ClassJobEntry : LodestoneParseable, IOptionalParseable 11 | { 12 | private readonly ClassJobEntryDefinition definition; 13 | 14 | /// 15 | /// Constructs a new class entry 16 | /// 17 | /// Root node of this entry 18 | /// Parser definition 19 | public ClassJobEntry(HtmlNode rootNode, ClassJobEntryDefinition definition) : base(rootNode) 20 | { 21 | this.definition = definition; 22 | } 23 | 24 | /// 25 | /// The name of this class and job combo. 26 | /// 27 | public string Name => ParseTooltip(this.definition.UnlockState); 28 | 29 | /// 30 | /// Value indicating whether this class has its job unlocked. 31 | /// 32 | public bool IsJobUnlocked => this.Name.Contains("/"); 33 | 34 | /// 35 | /// The level this class or job is at. 36 | /// 37 | public int Level 38 | { 39 | get 40 | { 41 | var level = Parse(this.definition.Level); 42 | return level == "-" ? 0 : int.Parse(level); 43 | } 44 | } 45 | 46 | private string ExpString => ParseInnerText(this.definition.Exp); 47 | 48 | private long? expCurrentVal; 49 | 50 | /// 51 | /// The amount of current achieved EXP on this level. 52 | /// 53 | public long ExpCurrent 54 | { 55 | get 56 | { 57 | if (!this.expCurrentVal.HasValue) 58 | ParseExp(); 59 | 60 | return this.expCurrentVal!.Value; 61 | } 62 | } 63 | 64 | private long? expMaxVal; 65 | 66 | /// 67 | /// The amount of EXP to be reached to gain the next level. 68 | /// 69 | public long ExpMax 70 | { 71 | get 72 | { 73 | if (!this.expCurrentVal.HasValue) 74 | ParseExp(); 75 | 76 | return this.expMaxVal!.Value; 77 | } 78 | } 79 | 80 | /// 81 | /// The outstanding amount of EXP to go to the next level. 82 | /// 83 | public long ExpToGo => this.ExpMax - this.ExpCurrent; 84 | 85 | private void ParseExp() 86 | { 87 | if (!this.Exists) 88 | { 89 | this.expCurrentVal = 0; 90 | this.expMaxVal = 0; 91 | 92 | return; 93 | } 94 | 95 | var expVals = this.ExpString.Split(" / ").Select(x => x.Replace(",", string.Empty)).ToArray(); 96 | 97 | if (expVals[0] == "--") 98 | { 99 | this.expCurrentVal = 0; 100 | this.expMaxVal = 0; 101 | 102 | return; 103 | } 104 | 105 | this.expCurrentVal = long.Parse(expVals[0]); 106 | this.expMaxVal = long.Parse(expVals[1]); 107 | } 108 | 109 | /// 110 | /// Value indicating whether this job, if DoH or DoL, is specialized. 111 | /// 112 | public bool IsSpecialized => ParseAttribute(this.definition.UnlockState, "class")?.Contains("--meister") ?? false; 113 | 114 | /// 115 | /// Value indicating if this class is unlocked. 116 | /// 117 | public bool Exists => this.Level != 0; 118 | 119 | /// 120 | /// Value indicating if this class is unlocked. 121 | /// 122 | public bool IsUnlocked => this.Exists; 123 | 124 | /// 125 | /// The string representation of this object. 126 | /// 127 | /// Name (Level) 128 | public override string ToString() => $"{this.Name} ({this.Level})"; 129 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Character/ClassJob/ClassJobEureka.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model.Character; 4 | 5 | namespace NetStone.Model.Parseables.Character.ClassJob; 6 | 7 | /// 8 | /// Entry for Eureka Field Operation of a character 9 | /// 10 | public class ClassJobEureka : LodestoneParseable, IOptionalParseable 11 | { 12 | private readonly ClassJobEurekaDefinition definition; 13 | 14 | /// 15 | /// Constructs a new class entry 16 | /// 17 | /// Root node of this entry 18 | /// Parser definition 19 | public ClassJobEureka(HtmlNode rootNode, ClassJobEurekaDefinition definition) : base(rootNode) 20 | { 21 | this.definition = definition; 22 | } 23 | 24 | /// 25 | /// The name of this class and job combo. 26 | /// 27 | public string Name => Parse(this.definition.Name); 28 | 29 | /// 30 | /// Value indicating whether this class has its job unlocked. 31 | /// 32 | public bool IsJobUnlocked => this.Name.Contains("/"); 33 | 34 | /// 35 | /// The level this class or job is at. 36 | /// 37 | public int Level => int.TryParse(Parse(this.definition.Level), out var level)? level : 0; 38 | 39 | private string ExpString => ParseInnerText(this.definition.Exp); 40 | 41 | private int? expCurrentVal; 42 | 43 | /// 44 | /// The amount of current achieved EXP on this level. 45 | /// 46 | public int ExpCurrent 47 | { 48 | get 49 | { 50 | if (!this.expCurrentVal.HasValue) 51 | ParseExp(); 52 | 53 | return this.expCurrentVal!.Value; 54 | } 55 | } 56 | 57 | private int? expMaxVal; 58 | 59 | /// 60 | /// The amount of EXP to be reached to gain the next level. 61 | /// 62 | public int ExpMax 63 | { 64 | get 65 | { 66 | if (!this.expCurrentVal.HasValue) 67 | ParseExp(); 68 | 69 | return this.expMaxVal!.Value; 70 | } 71 | } 72 | 73 | /// 74 | /// The outstanding amount of EXP to go to the next level. 75 | /// 76 | public int ExpToGo => this.ExpMax - this.ExpCurrent; 77 | 78 | private void ParseExp() 79 | { 80 | if (!this.Exists) 81 | { 82 | this.expCurrentVal = 0; 83 | this.expMaxVal = 0; 84 | 85 | return; 86 | } 87 | 88 | var expVals = this.ExpString.Split(" / ").Select(x => x.Replace(",", string.Empty)).ToArray(); 89 | 90 | this.expCurrentVal = int.TryParse(expVals[0], out var expCur) ? expCur : 0; 91 | this.expMaxVal = int.TryParse(expVals[1], out var expMax) ? expMax : 0; 92 | } 93 | 94 | /// 95 | /// Value indicating if this class is unlocked. 96 | /// 97 | public bool Exists => this.Level != 0; 98 | 99 | /// 100 | /// Value indicating if this class is unlocked. 101 | /// 102 | public bool IsUnlocked => this.Exists; 103 | 104 | /// 105 | /// The string representation of this object. 106 | /// 107 | /// Name (Level) 108 | public override string ToString() => $"{this.Name} ({this.Level})"; 109 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Character/Collectable/CharacterCollectable.cs: -------------------------------------------------------------------------------- 1 | using HtmlAgilityPack; 2 | using NetStone.Definitions.Model.Character; 3 | 4 | namespace NetStone.Model.Parseables.Character.Collectable; 5 | 6 | /// 7 | /// Models a collection of collectables for characters 8 | /// 9 | public class CharacterCollectable : LodestoneParseable 10 | { 11 | private readonly CharacterCollectableDefinition definition; 12 | 13 | /// 14 | /// Constructs a collectable collection 15 | /// 16 | /// Root node of list 17 | /// Parser definitions 18 | public CharacterCollectable(HtmlNode rootNode, CharacterCollectableDefinition definition) : base(rootNode) 19 | { 20 | this.definition = definition; 21 | } 22 | 23 | private CharacterCollectableEntry[]? parsedResults; 24 | 25 | /// 26 | /// All collectables collected by the character. 27 | /// 28 | public CharacterCollectableEntry[] Collectables 29 | { 30 | get 31 | { 32 | if (this.parsedResults == null) 33 | ParseCollectables(); 34 | 35 | return this.parsedResults!; 36 | } 37 | } 38 | 39 | private void ParseCollectables() 40 | { 41 | var nodes = QueryChildNodes(this.definition.GetDefinitions().Root); 42 | 43 | this.parsedResults = new CharacterCollectableEntry[nodes.Length]; 44 | for (var i = 0; i < this.parsedResults.Length; i++) 45 | { 46 | this.parsedResults[i] = new CharacterCollectableEntry(nodes[i], this.definition); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Character/Collectable/CharacterCollectableEntry.cs: -------------------------------------------------------------------------------- 1 | using HtmlAgilityPack; 2 | using NetStone.Definitions.Model.Character; 3 | 4 | namespace NetStone.Model.Parseables.Character.Collectable; 5 | 6 | /// 7 | /// Entry for collectable collection 8 | /// 9 | public class CharacterCollectableEntry : LodestoneParseable 10 | { 11 | private readonly CharacterCollectableDefinition definition; 12 | 13 | /// 14 | /// Constructs one collectable entry 15 | /// 16 | /// Root node for entry 17 | /// Parse definition 18 | public CharacterCollectableEntry(HtmlNode rootNode, CharacterCollectableDefinition definition) : base(rootNode) 19 | { 20 | this.definition = definition; 21 | } 22 | 23 | /// 24 | /// The name of this collectable. 25 | /// 26 | public string Name => Parse(this.definition.GetDefinitions().Name); 27 | 28 | /// 29 | /// The string representation of this collectable. 30 | /// 31 | /// 32 | public override string ToString() => this.Name; 33 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Character/FreeCompanySocialGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model.Character; 4 | using NetStone.Model.Parseables.FreeCompany; 5 | 6 | namespace NetStone.Model.Parseables.Character; 7 | 8 | /// 9 | /// Models a Free Company entry on the character profile 10 | /// 11 | public class FreeCompanySocialGroup : SocialGroup 12 | { 13 | private readonly LodestoneClient client; 14 | 15 | /// 16 | /// Constructs FC entry for profile page 17 | /// 18 | /// 19 | /// 20 | /// 21 | public FreeCompanySocialGroup(LodestoneClient client, HtmlNode rootNode, 22 | ICharacterSocialGroupDefinition socialGroupDefinition) : base(rootNode, socialGroupDefinition) 23 | { 24 | this.client = client; 25 | } 26 | 27 | /// 28 | /// Fetch the full details of this FC. 29 | /// 30 | /// object containing all details of the free company. 31 | public async Task GetDetails() => 32 | this.Id is null ? null : await this.client.GetFreeCompany(this.Id); 33 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Character/Gear/CharacterGear.cs: -------------------------------------------------------------------------------- 1 | using HtmlAgilityPack; 2 | using NetStone.Definitions.Model.Character; 3 | 4 | namespace NetStone.Model.Parseables.Character.Gear; 5 | 6 | /// 7 | /// Container class holding information about a character's equipped gear. 8 | /// 9 | public class CharacterGear : LodestoneParseable 10 | { 11 | private readonly LodestoneClient client; 12 | private readonly CharacterGearDefinition definition; 13 | 14 | /// 15 | /// Constructs parser for character gear 16 | /// 17 | /// 18 | /// 19 | /// 20 | public CharacterGear(LodestoneClient client, HtmlNode rootNode, CharacterGearDefinition definition) : base(rootNode) 21 | { 22 | this.client = client; 23 | this.definition = definition; 24 | } 25 | 26 | /// 27 | /// Information about the characters' weapon. Null if none equipped. 28 | /// 29 | public GearEntry? Mainhand => new GearEntry(this.client, this.RootNode, this.definition.Mainhand).GetOptional(); 30 | 31 | /// 32 | /// Information about the characters' shield/offhand. Null if none equipped. 33 | /// 34 | public GearEntry? Offhand => new GearEntry(this.client, this.RootNode, this.definition.Offhand).GetOptional(); 35 | 36 | /// 37 | /// Information about the characters' headgear. Null if none equipped. 38 | /// 39 | public GearEntry? Head => new GearEntry(this.client, this.RootNode, this.definition.Head).GetOptional(); 40 | 41 | /// 42 | /// Information about the characters' body gear. Null if none equipped. 43 | /// 44 | public GearEntry? Body => new GearEntry(this.client, this.RootNode, this.definition.Body).GetOptional(); 45 | 46 | /// 47 | /// Information about the characters' gloves. Null if none equipped. 48 | /// 49 | public GearEntry? Hands => new GearEntry(this.client, this.RootNode, this.definition.Hands).GetOptional(); 50 | 51 | /// 52 | /// Information about the characters' waist gear. Null if none equipped. 53 | /// 54 | public GearEntry? Waist => new GearEntry(this.client, this.RootNode, this.definition.Waist).GetOptional(); 55 | 56 | /// 57 | /// Information about the characters' pants. Null if none equipped. 58 | /// 59 | public GearEntry? Legs => new GearEntry(this.client, this.RootNode, this.definition.Legs).GetOptional(); 60 | 61 | /// 62 | /// Information about the characters' shoes. Null if none equipped. 63 | /// 64 | public GearEntry? Feet => new GearEntry(this.client, this.RootNode, this.definition.Feet).GetOptional(); 65 | 66 | /// 67 | /// Information about the characters' earrings. Null if none equipped. 68 | /// 69 | public GearEntry? Earrings => new GearEntry(this.client, this.RootNode, this.definition.Earrings).GetOptional(); 70 | 71 | /// 72 | /// Information about the characters' necklace. Null if none equipped. 73 | /// 74 | public GearEntry? Necklace => new GearEntry(this.client, this.RootNode, this.definition.Necklace).GetOptional(); 75 | 76 | /// 77 | /// Information about the characters' bracelets. Null if none equipped. 78 | /// 79 | public GearEntry? Bracelets => new GearEntry(this.client, this.RootNode, this.definition.Bracelets).GetOptional(); 80 | 81 | /// 82 | /// Information about the characters' first ring. Null if none equipped. 83 | /// 84 | public GearEntry? Ring1 => new GearEntry(this.client, this.RootNode, this.definition.Ring1).GetOptional(); 85 | 86 | /// 87 | /// Information about the characters' second ring. Null if none equipped. 88 | /// 89 | public GearEntry? Ring2 => new GearEntry(this.client, this.RootNode, this.definition.Ring2).GetOptional(); 90 | 91 | /// 92 | /// Information about the characters' soul crystal. Null if none equipped. 93 | /// 94 | public SoulcrystalEntry? Soulcrystal => 95 | new SoulcrystalEntry(this.RootNode, this.definition.Soulcrystal).GetOptional(); 96 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Character/Gear/GearEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using HtmlAgilityPack; 4 | using NetStone.Definitions.Model.Character; 5 | using NetStone.GameData; 6 | 7 | namespace NetStone.Model.Parseables.Character.Gear; 8 | 9 | /// 10 | /// Container class holding information about a gear slot. 11 | /// 12 | public class GearEntry : LodestoneParseable, IOptionalParseable 13 | { 14 | /// 15 | /// Character representing the high quality symbol 16 | /// 17 | public const char HqChar = '\uE03C'; 18 | 19 | private readonly LodestoneClient client; 20 | private readonly GearEntryDefinition definition; 21 | 22 | private NamedGameData? cachedGameData; 23 | 24 | /// 25 | /// Construct a new gear entry 26 | /// 27 | /// Lodestone client 28 | /// Entry node 29 | /// Parser definition 30 | public GearEntry(LodestoneClient client, HtmlNode rootNode, GearEntryDefinition definition) : base(rootNode) 31 | { 32 | this.client = client; 33 | this.definition = definition; 34 | } 35 | 36 | /// 37 | /// Link to this piece's Eorzea DB page. 38 | /// 39 | public Uri? ItemDatabaseLink => ParseHref(this.definition.DbLink); 40 | 41 | /// 42 | /// Name of this item. 43 | /// 44 | public string ItemName => Parse(this.definition.Name); 45 | 46 | /// 47 | /// Indicates if this item is high quality 48 | /// 49 | public bool IsHq => this.ItemName.EndsWith(HqChar); 50 | 51 | /// 52 | /// Returns the name of this item without high quality icon 53 | /// 54 | public string StrippedItemName => this.IsHq ? this.ItemName.Remove(this.ItemName.Length - 1) : this.ItemName; 55 | 56 | /// 57 | /// Link to the glamoured item's Eorzea DB page. 58 | /// 59 | public Uri? GlamourDatabaseLink => ParseHref(this.definition.MirageDbLink); 60 | 61 | /// 62 | /// Name of the glamoured item. 63 | /// 64 | public string GlamourName => Parse(this.definition.MirageName); 65 | 66 | /// 67 | /// Name of the dye applied to this item. 68 | /// 69 | //TODO: parse 70 | public string Stain => Parse(this.definition.Stain); 71 | 72 | /// 73 | /// Materia applied to this item. 74 | /// 75 | public string[] Materia => new[] 76 | { 77 | ParseDirectInnerText(this.definition.Materia1), 78 | ParseDirectInnerText(this.definition.Materia2), 79 | ParseDirectInnerText(this.definition.Materia3), 80 | ParseDirectInnerText(this.definition.Materia4), 81 | ParseDirectInnerText(this.definition.Materia5), 82 | }; 83 | 84 | /// 85 | /// Name of this item's crafter. 86 | /// 87 | public string CreatorName => Parse(this.definition.CreatorName); 88 | 89 | /// 90 | /// Item level of this item 91 | /// 92 | public int ItemLevel => int.TryParse(Parse(definition.ItemLevel).Split(' ').LastOrDefault(), out var itemLevel) 93 | ? itemLevel 94 | : 0; 95 | 96 | /// 97 | /// Indicating whether the item slot has an item equipped or not. 98 | /// 99 | public bool Exists => HasNode(this.definition.Name); 100 | 101 | /// 102 | /// Get game data representing this item 103 | /// 104 | /// Item data 105 | public NamedGameData? GetGameData() 106 | { 107 | this.cachedGameData ??= !this.Exists ? null : this.client.Data?.GetItem(this.ItemName); 108 | return this.cachedGameData; 109 | } 110 | 111 | /// 112 | /// String representation of the gear slot. 113 | /// 114 | /// The name of the item. 115 | public override string ToString() => this.ItemName; 116 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Character/Gear/SoulcrystalEntry.cs: -------------------------------------------------------------------------------- 1 | using HtmlAgilityPack; 2 | using NetStone.Definitions.Model.Character; 3 | using NetStone.Search.FreeCompany; 4 | using System; 5 | 6 | namespace NetStone.Model.Parseables.Character.Gear; 7 | 8 | /// 9 | /// Represents data about a character's soul crystal 10 | /// 11 | public class SoulcrystalEntry : LodestoneParseable, IOptionalParseable 12 | { 13 | private readonly SoulcrystalEntryDefinition definition; 14 | 15 | /// 16 | public SoulcrystalEntry(HtmlNode rootNode, SoulcrystalEntryDefinition definition) : base(rootNode) 17 | { 18 | this.definition = definition; 19 | } 20 | 21 | //public Uri ItemDatabaseLink => ParseHref(this.definition.Name); 22 | 23 | /// 24 | /// Name of the item 25 | /// 26 | public string ItemName => Parse(this.definition.Name); 27 | 28 | /// 29 | public bool Exists => HasNode(this.definition.Name); 30 | 31 | /// 32 | public override string ToString() => this.ItemName; 33 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/FreeCompany/FreeCompanyEstate.cs: -------------------------------------------------------------------------------- 1 | using HtmlAgilityPack; 2 | using NetStone.Definitions.Model.FreeCompany; 3 | 4 | namespace NetStone.Model.Parseables.FreeCompany; 5 | 6 | /// 7 | /// Information about the Free CCompany's estate 8 | /// 9 | public class FreeCompanyEstate : LodestoneParseable, IOptionalParseable 10 | { 11 | private readonly EstateDefinition definition; 12 | 13 | /// 14 | public FreeCompanyEstate(HtmlNode rootNode, EstateDefinition definition) : base(rootNode) 15 | { 16 | this.definition = definition; 17 | } 18 | 19 | /// 20 | /// Name of the estate 21 | /// 22 | public string Name => Parse(this.definition.Name); 23 | 24 | /// 25 | /// The greeting phrase for this estate 26 | /// 27 | public string Greeting => Parse(this.definition.Greeting); 28 | 29 | /// 30 | /// The plot where the estate is built 31 | /// 32 | public string Plot => Parse(this.definition.Plot); 33 | 34 | /// 35 | public bool Exists => !HasNode(this.definition.NoEstate); 36 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/FreeCompany/FreeCompanyFocus.cs: -------------------------------------------------------------------------------- 1 | using HtmlAgilityPack; 2 | using NetStone.Definitions.Model.FreeCompany; 3 | 4 | namespace NetStone.Model.Parseables.FreeCompany; 5 | 6 | /// 7 | /// Infomation about the content this FC focuses on 8 | /// 9 | public class FreeCompanyFocus : LodestoneParseable, IOptionalParseable 10 | { 11 | private readonly FreeCompanyFocusDefinition definition; 12 | 13 | /// 14 | public FreeCompanyFocus(HtmlNode rootNode, FreeCompanyFocusDefinition definition) : base(rootNode) 15 | { 16 | this.definition = definition; 17 | } 18 | 19 | /// 20 | /// Indicates that this FC has specified a focus 21 | /// 22 | public bool HasFocus => !HasNode(this.definition.NOTSPECIFIED); 23 | 24 | /// 25 | public bool Exists => this.HasFocus; 26 | 27 | /// 28 | /// Entry for this FC's focus on role play 29 | /// 30 | public FreeCompanyFocusEntry RolePlay => new(this.RootNode, this.definition.RolePlay); 31 | 32 | /// 33 | /// Entry for this FC's focus on leveling 34 | /// 35 | public FreeCompanyFocusEntry Leveling => new(this.RootNode, this.definition.Leveling); 36 | 37 | /// 38 | /// Entry for this FC's focus on casual content 39 | /// 40 | public FreeCompanyFocusEntry Casual => new(this.RootNode, this.definition.Casual); 41 | 42 | /// 43 | /// Entry for this FC's focus on hardcore content 44 | /// 45 | public FreeCompanyFocusEntry Hardcore => new(this.RootNode, this.definition.Hardcore); 46 | 47 | /// 48 | /// Entry for this FC's focus on dungeons 49 | /// 50 | public FreeCompanyFocusEntry Dungeons => new(this.RootNode, this.definition.Dungeons); 51 | 52 | /// 53 | /// Entry for this FC's focus on guild heists 54 | /// 55 | public FreeCompanyFocusEntry Guildhests => new(this.RootNode, this.definition.Guildhests); 56 | 57 | /// 58 | /// Entry for this FC's focus on trials 59 | /// 60 | public FreeCompanyFocusEntry Trials => new(this.RootNode, this.definition.Trials); 61 | 62 | /// 63 | /// Entry for this FC's focus on raiding 64 | /// 65 | public FreeCompanyFocusEntry Raids => new(this.RootNode, this.definition.Raids); 66 | 67 | /// 68 | /// Entry for this FC's focus on PvP 69 | /// 70 | public FreeCompanyFocusEntry PvP => new(this.RootNode, this.definition.PvP); 71 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/FreeCompany/FreeCompanyFocusEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model.FreeCompany; 4 | 5 | namespace NetStone.Model.Parseables.FreeCompany; 6 | 7 | /// 8 | /// Data about one type of Free Company focus 9 | /// 10 | public class FreeCompanyFocusEntry : LodestoneParseable 11 | { 12 | private readonly FreeCompanyFocusEntryDefinition definition; 13 | 14 | /// 15 | /// Construct instance to parse focus 16 | /// 17 | /// Node that contains relevant data 18 | /// Parse definition 19 | public FreeCompanyFocusEntry(HtmlNode rootNode, FreeCompanyFocusEntryDefinition definition) : base(rootNode) 20 | { 21 | this.definition = definition; 22 | } 23 | 24 | /// 25 | /// Name of the focus type 26 | /// 27 | public string Name => Parse(this.definition.NAME); 28 | 29 | /// 30 | /// Uri to icon 31 | /// 32 | public Uri? Icon => ParseImageSource(this.definition.ICON); 33 | 34 | /// 35 | /// Indicates this focus is selected 36 | /// 37 | public bool IsEnabled => string.IsNullOrEmpty(Parse(this.definition.STATUS)); 38 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/FreeCompany/FreeCompanyReputation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using HtmlAgilityPack; 5 | using NetStone.Definitions.Model.FreeCompany; 6 | using NetStone.StaticData; 7 | 8 | namespace NetStone.Model.Parseables.FreeCompany; 9 | 10 | /// 11 | /// Information on free companies 12 | /// 13 | public class FreeCompanyReputation : LodestoneParseable 14 | { 15 | private readonly FreeCompanyReputationDefinition definition; 16 | 17 | /// 18 | /// Creates Free Company reputation information 19 | /// 20 | /// 21 | /// 22 | public FreeCompanyReputation(HtmlNode rootNode, FreeCompanyReputationDefinition definition) : base(rootNode) 23 | { 24 | this.definition = definition; 25 | } 26 | 27 | /// 28 | /// Maelstrom 29 | /// 30 | public FreeCompanyReputationEntry Maelstrom => new(this.RootNode, this.definition.Maelstrom); 31 | 32 | /// 33 | /// Order of the Twin Adder 34 | /// 35 | public FreeCompanyReputationEntry Adders => new(this.RootNode, this.definition.Adders); 36 | 37 | /// 38 | /// Immortal Flames 39 | /// 40 | public FreeCompanyReputationEntry Flames => new(this.RootNode, this.definition.Flames); 41 | 42 | /// 43 | /// Returns the relevant . 44 | /// 45 | /// Grand Company 46 | /// Grand company reputation 47 | public FreeCompanyReputationEntry GrandCompanyRep(GrandCompany gc) => gc switch 48 | { 49 | GrandCompany.Maelstrom => this.Maelstrom, 50 | GrandCompany.OrderOfTheTwinAdder => this.Adders, 51 | GrandCompany.ImmortalFlames => this.Flames, 52 | _ => throw new ArgumentException("Unknown Grand Company"), 53 | }; 54 | 55 | /// 56 | /// Maps to . 57 | /// You should only access this once and store a reference. 58 | /// 59 | public Dictionary GrandCompanyDict => 60 | new() 61 | { 62 | { GrandCompany.Maelstrom, this.Maelstrom }, 63 | { GrandCompany.OrderOfTheTwinAdder, this.Adders }, 64 | { GrandCompany.ImmortalFlames, this.Flames }, 65 | }; 66 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/FreeCompany/FreeCompanyReputationEntry.cs: -------------------------------------------------------------------------------- 1 | using HtmlAgilityPack; 2 | using NetStone.Definitions.Model.FreeCompany; 3 | 4 | namespace NetStone.Model.Parseables.FreeCompany; 5 | 6 | /// 7 | /// Models reputation of a Free Company with on Grand Company 8 | /// 9 | public class FreeCompanyReputationEntry : LodestoneParseable 10 | { 11 | private readonly FreeCompanyReputationEntryDefinition definition; 12 | 13 | /// 14 | /// Constructs FC reputation with on GC 15 | /// 16 | /// 17 | /// 18 | public FreeCompanyReputationEntry(HtmlNode rootNode, FreeCompanyReputationEntryDefinition definition) : 19 | base(rootNode) 20 | { 21 | this.definition = definition; 22 | } 23 | 24 | /// 25 | /// Name of the Grand Company 26 | /// 27 | public string Name => Parse(this.definition.Name); 28 | 29 | /// 30 | /// Progress to next rank 31 | /// 32 | public int Progress => int.Parse(Parse(this.definition.Progress)); 33 | 34 | /// 35 | /// Current rank 36 | /// 37 | public string Rank => Parse(this.definition.Rank); 38 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/FreeCompany/LodestoneFreeCompany.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using HtmlAgilityPack; 4 | using NetStone.Definitions; 5 | using NetStone.Definitions.Model.FreeCompany; 6 | using NetStone.Model.Parseables.FreeCompany.Members; 7 | using NetStone.Search.FreeCompany; 8 | 9 | namespace NetStone.Model.Parseables.FreeCompany; 10 | 11 | /// 12 | /// Provides information of a Free Company 13 | /// 14 | public class LodestoneFreeCompany : LodestoneParseable 15 | { 16 | private readonly LodestoneClient client; 17 | 18 | private readonly FreeCompanyDefinition fcDefinition; 19 | private readonly FreeCompanyFocusDefinition focusDefinition; 20 | private readonly FreeCompanyReputationDefinition reputationDefinition; 21 | 22 | /// 23 | /// Constructs Free Company information parser 24 | /// 25 | /// Current client 26 | /// Root node of FC page 27 | /// Parser definitions 28 | /// Id of FC 29 | public LodestoneFreeCompany(LodestoneClient client, HtmlNode rootNode, DefinitionsContainer definitions, string id) 30 | : base(rootNode) 31 | { 32 | this.client = client; 33 | this.Id = id; 34 | 35 | this.fcDefinition = definitions.FreeCompany; 36 | this.focusDefinition = definitions.FreeCompanyFocus; 37 | this.reputationDefinition = definitions.FreeCompanyReputation; 38 | } 39 | 40 | /// 41 | /// Name of this FC 42 | /// 43 | public string Name => Parse(this.fcDefinition.Name); 44 | 45 | /// 46 | /// Id of this FC 47 | /// 48 | public string Id { get; } 49 | 50 | /// 51 | /// Slogan 52 | /// 53 | public string Slogan => Parse(this.fcDefinition.Slogan); 54 | 55 | /// 56 | /// Tag 57 | /// 58 | public string Tag => Parse(this.fcDefinition.Tag); 59 | 60 | /// 61 | /// FC Icon/Crest 62 | /// 63 | public IconLayers CrestLayers => new(this.RootNode, this.fcDefinition.CrestLayers); 64 | 65 | /// 66 | /// Formation date 67 | /// 68 | public DateTime Formed => ParseTime(this.fcDefinition.Formed); 69 | 70 | /// 71 | /// Current GC 72 | /// 73 | public string GrandCompany => Parse(this.fcDefinition.GrandCompany).TrimEnd(); 74 | 75 | /// 76 | /// Current Rank 77 | /// 78 | public int Rank => int.Parse(Parse(this.fcDefinition.Rank)); 79 | 80 | /// 81 | /// Monthly ranking 82 | /// 83 | public int? RankingMonthly => int.TryParse(Parse(this.fcDefinition.Ranking.Monthly), out var result) ? result : null; 84 | 85 | /// 86 | /// Weekly ranking 87 | /// 88 | public int? RankingWeekly => int.TryParse(Parse(this.fcDefinition.Ranking.Weekly), out var result) ? result : null; 89 | 90 | /// 91 | /// Recruitment status 92 | /// 93 | public string Recruitment => Parse(this.fcDefinition.Recruitment); 94 | 95 | /// 96 | /// Number of active members 97 | /// 98 | public int ActiveMemberCount => int.Parse(Parse(this.fcDefinition.ActiveMemberCount)); 99 | 100 | /// 101 | /// Activity status 102 | /// 103 | //todo: selector does not work 104 | public string ActiveState => Parse(this.fcDefinition.Activestate).Trim(); 105 | 106 | 107 | /// 108 | /// Information about the estate 109 | /// 110 | public FreeCompanyEstate? Estate => 111 | new FreeCompanyEstate(this.RootNode, this.fcDefinition.EstateDefinition).GetOptional(); 112 | 113 | /// 114 | /// Information about focused gameplay 115 | /// 116 | public FreeCompanyFocus? Focus => new FreeCompanyFocus(this.RootNode, this.focusDefinition).GetOptional(); 117 | 118 | /// 119 | /// Reputation with the Grand Companies 120 | /// 121 | public FreeCompanyReputation Reputation => new(this.RootNode, this.reputationDefinition); 122 | 123 | /// 124 | /// Home World 125 | /// 126 | public string World => Parse(this.fcDefinition.Server); 127 | 128 | /// 129 | /// Fetches all members of this FC 130 | /// 131 | /// Members 132 | public async Task GetMembers() => await this.client.GetFreeCompanyMembers(this.Id); 133 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/FreeCompany/Members/FreeCompanyMembers.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model; 4 | using NetStone.Definitions.Model.FreeCompany; 5 | 6 | namespace NetStone.Model.Parseables.FreeCompany.Members; 7 | 8 | /// 9 | /// Information about a Free Company's members 10 | /// 11 | public class FreeCompanyMembers : PaginatedIdResult 12 | { 13 | /// 14 | /// Constructs member list 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | public FreeCompanyMembers(LodestoneClient client, HtmlNode rootNode, PagedDefinition definition, string id) : 21 | base(rootNode, definition, client.GetFreeCompanyMembers, id) 22 | { 23 | } 24 | 25 | /// 26 | /// Lists all members 27 | /// 28 | public IEnumerable Members => this.Results; 29 | 30 | /// 31 | protected override FreeCompanyMembersEntry[] ParseResults() 32 | { 33 | var nodes = QueryContainer(this.PageDefinition); 34 | 35 | var parsedResults = new FreeCompanyMembersEntry[nodes.Length]; 36 | for (var i = 0; i < parsedResults.Length; i++) 37 | { 38 | parsedResults[i] = new FreeCompanyMembersEntry(nodes[i], this.PageDefinition.Entry); 39 | } 40 | return parsedResults; 41 | } 42 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/FreeCompany/Members/FreeCompanyMembersEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model.FreeCompany; 4 | 5 | namespace NetStone.Model.Parseables.FreeCompany.Members; 6 | 7 | /// 8 | /// Models one entry for the Free Company member list 9 | /// 10 | public class FreeCompanyMembersEntry : LodestoneParseable 11 | { 12 | private readonly FreeCompanyMembersEntryDefinition definition; 13 | 14 | /// 15 | public FreeCompanyMembersEntry(HtmlNode rootNode, FreeCompanyMembersEntryDefinition definition) : base(rootNode) 16 | { 17 | this.definition = definition; 18 | } 19 | 20 | /// 21 | /// Name of character 22 | /// 23 | public string Name => Parse(this.definition.Name); 24 | 25 | /// 26 | /// Id of character 27 | /// 28 | public string Id => Parse(this.definition.Id); 29 | 30 | /// 31 | /// Rank with character's Grand Company 32 | /// 33 | public string Rank => Parse(this.definition.Rank); 34 | 35 | /// 36 | /// Icon representing 37 | /// 38 | public Uri? RankIcon => ParseImageSource(this.definition.RankIcon); 39 | 40 | /// 41 | /// Rank with character's Free Company 42 | /// 43 | public string FreeCompanyRank => Parse(this.definition.FreeCompanyRank); 44 | 45 | /// 46 | /// Icon representing 47 | /// 48 | public Uri? FreeCompanyRankIcon => ParseImageSource(this.definition.FreeCompanyRankIcon); 49 | 50 | /// 51 | /// Home world 52 | /// 53 | public string Server => ParseRegex(this.definition.Server)["World"].Value; 54 | 55 | /// 56 | /// Data center 57 | /// 58 | public string Datacenter => ParseRegex(this.definition.Server)["DC"].Value; 59 | 60 | /// 61 | /// Character's avatar 62 | /// 63 | public Uri? Avatar => ParseImageSource(this.definition.Avatar); 64 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/IconLayers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model; 4 | 5 | namespace NetStone.Model.Parseables; 6 | 7 | /// 8 | /// Container class holding information about a social group's icon. 9 | /// 10 | public class IconLayers : LodestoneParseable 11 | { 12 | private readonly IconLayersDefinition definition; 13 | 14 | /// 15 | public IconLayers(HtmlNode rootNode, IconLayersDefinition definition) : base(rootNode) 16 | { 17 | this.definition = definition; 18 | } 19 | 20 | /// 21 | /// Link to the top layer image of the icon. 22 | /// 23 | public Uri? TopLayer => ParseImageSource(this.definition.Top); 24 | 25 | /// 26 | /// Link to the top layer image of the icon. 27 | /// 28 | public Uri? MiddleLayer => ParseImageSource(this.definition.Middle); 29 | 30 | /// 31 | /// Link to the top layer image of the icon. 32 | /// 33 | public Uri? BottomLayer => ParseImageSource(this.definition.Bottom); 34 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Linkshell/LodestoneLinkshell.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions; 4 | using NetStone.Definitions.Model.Linkshell; 5 | using NetStone.Model.Parseables.Linkshell.Members; 6 | 7 | namespace NetStone.Model.Parseables.Linkshell; 8 | 9 | /// 10 | /// Container class holding information about a linkshell and it's members. 11 | /// 12 | public class LodestoneLinkshell : PaginatedIdResult 13 | { 14 | private readonly LinkshellDefinition lsDefinition; 15 | 16 | /// 17 | /// Container class for a parseable linkshell page. 18 | /// 19 | /// The to be used to fetch further information. 20 | /// The root document node of the page. 21 | /// The holding definitions to be used to access data. 22 | /// The ID of the cross world linkshell. 23 | public LodestoneLinkshell(LodestoneClient client, HtmlNode rootNode, DefinitionsContainer container, string id) : base(rootNode,container.LinkshellMember, client.GetLinkshell,id) 24 | { 25 | this.lsDefinition = container.Linkshell; 26 | } 27 | 28 | /// 29 | /// Name 30 | /// 31 | public string Name => Parse(this.lsDefinition.Name); 32 | 33 | /// 34 | /// List of members 35 | /// 36 | public IEnumerable Members => this.Results; 37 | 38 | /// 39 | protected override LinkshellMemberEntry[] ParseResults() 40 | { 41 | var nodes = QueryContainer(this.PageDefinition); 42 | 43 | var parsedResults = new LinkshellMemberEntry[nodes.Length]; 44 | for (var i = 0; i < parsedResults.Length; i++) 45 | { 46 | parsedResults[i] = new LinkshellMemberEntry(nodes[i], this.PageDefinition.Entry); 47 | } 48 | return parsedResults; 49 | } 50 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Linkshell/Members/LinkshellMemberEntry.cs: -------------------------------------------------------------------------------- 1 | using HtmlAgilityPack; 2 | using NetStone.Definitions.Model.Linkshell; 3 | 4 | namespace NetStone.Model.Parseables.Linkshell.Members; 5 | 6 | /// 7 | /// Container class holding information about a linkshell member. 8 | /// 9 | public class LinkshellMemberEntry : LodestoneParseable 10 | { 11 | private readonly LinkshellMemberEntryDefinition definition; 12 | /// 13 | /// Create instance of member entry for a given node 14 | /// 15 | /// Root html node of this entry 16 | /// Css and regex definition 17 | public LinkshellMemberEntry(HtmlNode rootNode, LinkshellMemberEntryDefinition definition) : base(rootNode) 18 | { 19 | this.definition = definition; 20 | } 21 | 22 | /// 23 | /// Avatar 24 | /// 25 | public string Avatar => Parse(this.definition.Avatar); 26 | 27 | /// 28 | /// ID 29 | /// 30 | public string? Id => ParseHrefId(this.definition.Id); 31 | 32 | /// 33 | /// Name 34 | /// 35 | public string Name => Parse(this.definition.Name); 36 | 37 | /// 38 | /// Rank 39 | /// 40 | public string Rank => Parse(this.definition.Rank); 41 | 42 | /// 43 | /// Rank Icon 44 | /// 45 | public string RankIcon => Parse(this.definition.RankIcon); 46 | 47 | /// 48 | /// Linkshell rank 49 | /// 50 | public string LinkshellRank => Parse(this.definition.LinkshellRank); 51 | 52 | /// 53 | /// Linkshell rank Icon 54 | /// 55 | public string LinkshellRankIcon => Parse(this.definition.LinkshellRankIcon); 56 | 57 | /// 58 | /// Server 59 | /// 60 | public string Server => Parse(this.definition.Server); 61 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Search/CWLS/CrossworldLinkshellSearchEntry.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model.CWLS; 4 | using NetStone.Model.Parseables.CWLS; 5 | 6 | namespace NetStone.Model.Parseables.Search.CWLS; 7 | 8 | /// 9 | /// Models one entry in the cwls search results list 10 | /// 11 | public class CrossworldLinkshellSearchEntry : LodestoneParseable 12 | { 13 | private readonly LodestoneClient client; 14 | private readonly CrossworldLinkshellSearchEntryDefinition definition; 15 | 16 | /// 17 | public CrossworldLinkshellSearchEntry(LodestoneClient client, HtmlNode rootNode, CrossworldLinkshellSearchEntryDefinition definition) : 18 | base(rootNode) 19 | { 20 | this.client = client; 21 | this.definition = definition; 22 | } 23 | 24 | /// 25 | /// Character name 26 | /// 27 | public string Name => Parse(this.definition.Name); 28 | 29 | /// 30 | /// Lodestone Id 31 | /// 32 | public string? Id => ParseHrefId(this.definition.Id); 33 | 34 | /// 35 | /// Datacenter 36 | /// 37 | public string DataCenter => Parse(this.definition.Dc); 38 | 39 | 40 | /// 41 | /// Number of active members 42 | /// 43 | public int ActiveMembers => int.TryParse(Parse(this.definition.ActiveMembers), out var parsed) ? parsed : -1; 44 | 45 | /// 46 | /// Fetch cross world link shell 47 | /// 48 | /// Task of retrieving cwls 49 | public async Task GetCrossworldLinkshell() => 50 | this.Id is null ? null : await this.client.GetCrossworldLinkshell(this.Id); 51 | 52 | /// 53 | public override string ToString() => this.Name; 54 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Search/CWLS/CrossworldLinkshellSearchPage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model; 4 | using NetStone.Definitions.Model.CWLS; 5 | using NetStone.Search.Linkshell; 6 | 7 | namespace NetStone.Model.Parseables.Search.CWLS; 8 | 9 | /// 10 | /// Models cross world link shell search results 11 | /// 12 | public class CrossworldLinkshellSearchPage 13 | : PaginatedSearchResult 15 | { 16 | private readonly LodestoneClient client; 17 | /// 18 | /// Constructs character search results 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | public CrossworldLinkshellSearchPage(LodestoneClient client, HtmlNode rootNode, 25 | PagedDefinition pageDefinition, 26 | CrossworldLinkshellSearchQuery currentQuery) 27 | : base(rootNode, pageDefinition, client.SearchCrossworldLinkshell, currentQuery) 28 | { 29 | this.client = client; 30 | } 31 | 32 | 33 | /// 34 | /// List all results 35 | /// 36 | public new IEnumerable Results => base.Results; 37 | 38 | /// 39 | protected override CrossworldLinkshellSearchEntry[] ParseResults() 40 | { 41 | var container = QueryContainer(this.PageDefinition); 42 | 43 | var parsedResults = new CrossworldLinkshellSearchEntry[container.Length]; 44 | for (var i = 0; i < parsedResults.Length; i++) 45 | { 46 | parsedResults[i] = new CrossworldLinkshellSearchEntry(this.client, container[i], this.PageDefinition.Entry); 47 | } 48 | return parsedResults; 49 | } 50 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Search/Character/CharacterSearchEntry.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model.Character; 4 | using NetStone.Model.Parseables.Character; 5 | 6 | namespace NetStone.Model.Parseables.Search.Character; 7 | 8 | /// 9 | /// Models one entry in the character search results list 10 | /// 11 | public class CharacterSearchEntry : LodestoneParseable 12 | { 13 | private readonly LodestoneClient client; 14 | private readonly CharacterSearchEntryDefinition definition; 15 | 16 | /// 17 | public CharacterSearchEntry(LodestoneClient client, HtmlNode rootNode, CharacterSearchEntryDefinition definition) : 18 | base(rootNode) 19 | { 20 | this.client = client; 21 | this.definition = definition; 22 | } 23 | 24 | /// 25 | /// Character name 26 | /// 27 | public string Name => Parse(this.definition.Name); 28 | 29 | /// 30 | /// Lodestone Id 31 | /// 32 | public string? Id => ParseHrefId(this.definition.Id); 33 | 34 | /// 35 | /// Fetch character profile 36 | /// 37 | /// Task of retrieving character 38 | public async Task GetCharacter() => 39 | this.Id is null ? null : await this.client.GetCharacter(this.Id); 40 | 41 | /// 42 | public override string ToString() => this.Name; 43 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Search/Character/CharacterSearchPage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model; 4 | using NetStone.Definitions.Model.Character; 5 | using NetStone.Search.Character; 6 | 7 | namespace NetStone.Model.Parseables.Search.Character; 8 | 9 | /// 10 | /// Models character search results 11 | /// 12 | public class CharacterSearchPage : PaginatedSearchResult 14 | { 15 | private readonly LodestoneClient client; 16 | 17 | /// 18 | /// Constructs character search results 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | public CharacterSearchPage(LodestoneClient client, HtmlNode rootNode, 25 | PagedDefinition pageDefinition, 26 | CharacterSearchQuery currentQuery) 27 | : base(rootNode, pageDefinition, client.SearchCharacter, currentQuery) 28 | { 29 | this.client = client; 30 | } 31 | 32 | /// 33 | /// List all results 34 | /// 35 | public new IEnumerable Results => base.Results; 36 | 37 | /// 38 | protected override CharacterSearchEntry[] ParseResults() 39 | { 40 | var container = QueryContainer(this.PageDefinition); 41 | 42 | var parsedResults = new CharacterSearchEntry[container.Length]; 43 | for (var i = 0; i < parsedResults.Length; i++) 44 | { 45 | parsedResults[i] = new CharacterSearchEntry(this.client, container[i], this.PageDefinition.Entry); 46 | } 47 | 48 | return parsedResults; 49 | } 50 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Search/FreeCompany/FreeCompanySearchEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using HtmlAgilityPack; 4 | using NetStone.Definitions.Model.FreeCompany; 5 | using NetStone.Model.Parseables.FreeCompany; 6 | using NetStone.Search.FreeCompany; 7 | 8 | namespace NetStone.Model.Parseables.Search.FreeCompany; 9 | 10 | /// 11 | /// Models on entry of the free company search results 12 | /// 13 | public class FreeCompanySearchEntry : LodestoneParseable 14 | { 15 | private readonly LodestoneClient client; 16 | private readonly FreeCompanySearchEntryDefinition definition; 17 | 18 | /// 19 | public FreeCompanySearchEntry(LodestoneClient client, HtmlNode rootNode, 20 | FreeCompanySearchEntryDefinition definition) : base(rootNode) 21 | { 22 | this.client = client; 23 | this.definition = definition; 24 | } 25 | 26 | /// 27 | /// Free Company name 28 | /// 29 | public string Name => Parse(this.definition.Name); 30 | 31 | /// 32 | /// Free company Id 33 | /// 34 | public string? Id => ParseHrefId(this.definition.Id); 35 | 36 | /// 37 | /// Home world of the FC 38 | /// 39 | public string Server => ParseRegex(this.definition.Server)["World"].Value; 40 | 41 | /// 42 | /// Data center of the FC 43 | /// 44 | public string Datacenter => ParseRegex(this.definition.Server)["DC"].Value; 45 | 46 | /// 47 | /// FC crest/icon 48 | /// 49 | public IconLayers CrestLayers => new(this.RootNode, this.definition.CrestLayers); 50 | 51 | /// 52 | /// Formation date 53 | /// 54 | public DateTime Formed => ParseTime(this.definition.Formed); 55 | 56 | /// 57 | /// Active status 58 | /// 59 | public ActiveTimes Active => this.ActiveText switch 60 | { 61 | "Always" => ActiveTimes.Always, 62 | "Weekends" => ActiveTimes.WeekendsOnly, 63 | "Weekdays" => ActiveTimes.WeekdaysOnly, 64 | "Not specified" => ActiveTimes.All, 65 | { } s => throw new ArgumentOutOfRangeException(s), 66 | }; 67 | 68 | /// 69 | /// Full text of active times 70 | /// 71 | //ToDo: fix regex 72 | public string ActiveText => ParseInnerText(this.definition.Active)[8..]; 73 | 74 | /// 75 | /// Active member count 76 | /// 77 | public int ActiveMembers => int.Parse(Parse(this.definition.ActiveMembers)); 78 | 79 | /// 80 | /// Recruitment status 81 | /// 82 | public bool RecruitmentOpen => Parse(this.definition.RecruitmentOpen) == "Open"; 83 | 84 | /// 85 | /// Affiliated grand company 86 | /// 87 | public string GrandCompany => Parse(this.definition.GrandCompany); 88 | 89 | /// 90 | /// Estate status 91 | /// 92 | public Housing EstateBuild => Parse(this.definition.EstateBuilt) switch 93 | { 94 | "No Estate or Plot" => Housing.NoEstateOrPlot, 95 | "Estate Built" => Housing.EstateBuilt, 96 | "Plot Only" => Housing.PlotOnly, 97 | _ => throw new ArgumentOutOfRangeException(), 98 | }; 99 | 100 | /// 101 | /// Retrieve Free Company profile 102 | /// 103 | /// Full FC profile 104 | public async Task GetFreeCompany() => 105 | this.Id is null ? null : await this.client.GetFreeCompany(this.Id); 106 | 107 | /// 108 | public override string ToString() => this.Name; 109 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Search/FreeCompany/FreeCompanySearchPage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model; 4 | using NetStone.Definitions.Model.FreeCompany; 5 | using NetStone.Search.FreeCompany; 6 | 7 | namespace NetStone.Model.Parseables.Search.FreeCompany; 8 | 9 | /// 10 | /// Models Free Company search results 11 | /// 12 | public class FreeCompanySearchPage : PaginatedSearchResult 14 | { 15 | private readonly LodestoneClient client; 16 | 17 | /// 18 | /// Constructs Free Company Search results 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | public FreeCompanySearchPage(LodestoneClient client, HtmlNode rootNode, 25 | PagedDefinition pageDefinition, 26 | FreeCompanySearchQuery currentQuery) 27 | : base(rootNode, pageDefinition, client.SearchFreeCompany, currentQuery) 28 | { 29 | this.client = client; 30 | } 31 | 32 | /// 33 | /// Lists all search results 34 | /// 35 | public new IEnumerable Results => base.Results; 36 | 37 | /// 38 | protected override FreeCompanySearchEntry[] ParseResults() 39 | { 40 | var container = QueryContainer(this.PageDefinition); 41 | 42 | var parsedResults = new FreeCompanySearchEntry[container.Length]; 43 | for (var i = 0; i < parsedResults.Length; i++) 44 | { 45 | parsedResults[i] = new FreeCompanySearchEntry(this.client, container[i], this.PageDefinition.Entry); 46 | } 47 | return parsedResults; 48 | } 49 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Search/Linkshell/LinkshellSearchEntry.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model.Linkshell; 4 | using NetStone.Model.Parseables.Linkshell; 5 | 6 | namespace NetStone.Model.Parseables.Search.Linkshell; 7 | 8 | /// 9 | /// Models one entry in the linkshell search results list 10 | /// 11 | public class LinkshellSearchEntry : LodestoneParseable 12 | { 13 | private readonly LodestoneClient client; 14 | private readonly LinkshellSearchEntryDefinition definition; 15 | 16 | /// 17 | public LinkshellSearchEntry(LodestoneClient client, HtmlNode rootNode, LinkshellSearchEntryDefinition definition) : 18 | base(rootNode) 19 | { 20 | this.client = client; 21 | this.definition = definition; 22 | } 23 | 24 | /// 25 | /// Character name 26 | /// 27 | public string Name => Parse(this.definition.Name); 28 | 29 | /// 30 | /// Lodestone Id 31 | /// 32 | public string? Id => ParseHrefId(this.definition.Id); 33 | 34 | /// 35 | /// Homeworld / Server 36 | /// 37 | public string HomeWorld => Parse(this.definition.Server); 38 | 39 | /// 40 | /// Number of active members 41 | /// 42 | public int ActiveMembers => int.TryParse(Parse(this.definition.ActiveMembers), out var parsed) ? parsed : -1; 43 | 44 | /// 45 | /// Fetch character profile 46 | /// 47 | /// Task of retrieving character 48 | public async Task GetLinkshell() => 49 | this.Id is null ? null : await this.client.GetLinkshell(this.Id); 50 | 51 | /// 52 | public override string ToString() => this.Name; 53 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/Search/Linkshell/LinkshellSearchPage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model; 4 | using NetStone.Definitions.Model.Linkshell; 5 | using NetStone.Search.Linkshell; 6 | 7 | namespace NetStone.Model.Parseables.Search.Linkshell; 8 | 9 | /// 10 | /// Models link shell search results 11 | /// 12 | public class LinkshellSearchPage : PaginatedSearchResult 14 | { 15 | private readonly LodestoneClient client; 16 | 17 | /// 18 | /// Constructs character search results 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | public LinkshellSearchPage(LodestoneClient client, HtmlNode rootNode, 25 | PagedDefinition pageDefinition, 26 | LinkshellSearchQuery currentQuery) 27 | : base(rootNode, pageDefinition, client.SearchLinkshell, currentQuery) 28 | { 29 | this.client = client; 30 | } 31 | 32 | /// 33 | /// List all results 34 | /// 35 | public new IEnumerable Results => base.Results; 36 | 37 | /// 38 | protected override LinkshellSearchEntry[] ParseResults() 39 | { 40 | var container = QueryContainer(this.PageDefinition); 41 | 42 | var parsedResults = new LinkshellSearchEntry[container.Length]; 43 | for (var i = 0; i < parsedResults.Length; i++) 44 | { 45 | parsedResults[i] = new LinkshellSearchEntry(this.client, container[i], this.PageDefinition.Entry); 46 | } 47 | return parsedResults; 48 | } 49 | } -------------------------------------------------------------------------------- /NetStone/Model/Parseables/SocialGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HtmlAgilityPack; 3 | using NetStone.Definitions.Model.Character; 4 | 5 | namespace NetStone.Model.Parseables; 6 | 7 | /// 8 | /// Models a group of players/characters 9 | /// 10 | public class SocialGroup : LodestoneParseable, IOptionalParseable 11 | { 12 | private readonly ICharacterSocialGroupDefinition definition; 13 | 14 | /// 15 | public SocialGroup(HtmlNode rootNode, ICharacterSocialGroupDefinition socialGroupDefinition) : base(rootNode) 16 | { 17 | this.definition = socialGroupDefinition; 18 | } 19 | 20 | /// 21 | /// Indicating whether this social group exists or not. 22 | /// 23 | public bool Exists => this.Id != null; 24 | 25 | /// 26 | /// Name of this social group. 27 | /// 28 | public string Name => ParseInnerText(this.definition.Name, true); 29 | 30 | /// 31 | /// ID of this social group. 32 | /// 33 | public string? Id => ParseHrefId(this.definition.Name); 34 | 35 | /// 36 | /// Link to this social group's page. 37 | /// 38 | public Uri? Link => ParseHref(this.definition.Name); 39 | 40 | /// 41 | /// of this social group's icon. 42 | /// 43 | public IconLayers IconLayers => new(this.RootNode, this.definition.IconLayers); 44 | 45 | /// 46 | /// String representation of the gear slot. 47 | /// 48 | /// The name of the item. 49 | public override string ToString() => this.Name; 50 | } -------------------------------------------------------------------------------- /NetStone/NetStone.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | 6 | NetStone 7 | 1.4.1 8 | 1.4.1 9 | Portable and modern Lodestone library. 10 | goaaats, Koenari 11 | https://github.com/xivapi/NetStone 12 | https://github.com/xivapi/NetStone 13 | 14 | LICENSE 15 | README.md 16 | 17 | true 18 | true 19 | 20 | 10.0 21 | enable 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | true 32 | true 33 | NetStone.xml 34 | snupkg 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /NetStone/Search/Character/CharacterSearchQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using NetStone.StaticData; 4 | 5 | namespace NetStone.Search.Character; 6 | 7 | /// 8 | /// Models a search for characters 9 | /// 10 | public class CharacterSearchQuery : ISearchQuery 11 | { 12 | /// 13 | /// Name to search 14 | /// 15 | public string CharacterName { get; set; } = ""; 16 | 17 | /// 18 | /// Homeworld for search 19 | /// 20 | public string World { get; set; } = ""; 21 | 22 | /// 23 | /// Datacenter to search 24 | /// 25 | public string DataCenter { get; set; } = ""; 26 | 27 | /// 28 | /// Role to search for 29 | /// 30 | public Role Role { get; set; } 31 | 32 | /// 33 | /// Job to search for 34 | /// 35 | public ClassJob ClassJob { get; set; } 36 | 37 | /// 38 | /// Race to search for 39 | /// 40 | public Race Race { get; set; } 41 | 42 | /// 43 | /// Tribe/Clan to search for 44 | /// 45 | public Tribe Tribe { get; set; } 46 | 47 | /// 48 | /// Grand Company affiliation to search for 49 | /// 50 | public GrandCompany GrandCompany { get; set; } = GrandCompany.None; 51 | 52 | /// 53 | /// Languages to include 54 | /// 55 | public Language Language { get; set; } = 56 | Language.Japanese | Language.English | Language.German | Language.French; 57 | 58 | /// 59 | /// Sort order 60 | /// 61 | public SortKind SortKind { get; set; } = SortKind.NameAtoZ; 62 | 63 | /// 64 | /// Construct search string to append to uri. 65 | /// Throws if incompatible query parameters are used 66 | /// 67 | /// Query string 68 | /// 69 | public string BuildQueryString() 70 | { 71 | if (string.IsNullOrEmpty(this.CharacterName)) 72 | throw new ArgumentException("CharacterName must not be empty or null.", nameof(this.CharacterName)); 73 | 74 | if (!string.IsNullOrEmpty(this.World) && !string.IsNullOrEmpty(this.DataCenter)) 75 | throw new ArgumentException( 76 | "You cannot specify World and DataCenter at the same time in one search query."); 77 | 78 | if (this.Role != Role.None && this.ClassJob != ClassJob.None) 79 | throw new ArgumentException( 80 | "You cannot specify Role and ClassJob at the same time in one search query."); 81 | 82 | if (this.Race != Race.None && this.Tribe != Tribe.None) 83 | throw new ArgumentException( 84 | "You cannot specify Race and Tribe at the same time in one search query."); 85 | 86 | var query = new StringBuilder(); 87 | 88 | query.Append($"?q={this.CharacterName}"); 89 | 90 | 91 | if (!string.IsNullOrEmpty(this.World)) 92 | query.Append($"&worldname={this.World}"); 93 | 94 | if (!string.IsNullOrEmpty(this.DataCenter)) 95 | query.Append($"&worldname=_dc_{this.DataCenter}"); 96 | 97 | 98 | if (this.Role != Role.None) 99 | query.Append($"&classjob=_job_{this.Role.ToString().ToUpperInvariant()}"); 100 | 101 | if (this.ClassJob != ClassJob.None) 102 | query.Append($"&classjob={(int)this.ClassJob}"); 103 | 104 | 105 | if (this.Tribe != Tribe.None) 106 | query.Append($"&race_tribe=tribe_{(int)this.Tribe}"); 107 | 108 | if (this.Race != Race.None) 109 | query.Append($"&race_tribe=race_{(int)this.Race}"); 110 | 111 | 112 | if (this.GrandCompany.HasFlag(GrandCompany.NoAffiliation)) 113 | query.Append("&gcid=0"); 114 | 115 | if (this.GrandCompany.HasFlag(GrandCompany.Maelstrom)) 116 | query.Append("&gcid=1"); 117 | 118 | if (this.GrandCompany.HasFlag(GrandCompany.OrderOfTheTwinAdder)) 119 | query.Append("&gcid=2"); 120 | 121 | if (this.GrandCompany.HasFlag(GrandCompany.ImmortalFlames)) 122 | query.Append("&gcid=3"); 123 | 124 | 125 | if (this.Language.HasFlag(Language.Japanese)) 126 | query.Append("&blog_lang=ja"); 127 | 128 | if (this.Language.HasFlag(Language.English)) 129 | query.Append("&blog_lang=en"); 130 | 131 | if (this.Language.HasFlag(Language.German)) 132 | query.Append("&blog_lang=de"); 133 | 134 | if (this.Language.HasFlag(Language.French)) 135 | query.Append("&blog_lang=fr"); 136 | 137 | 138 | query.Append($"&order={(int)this.SortKind}"); 139 | 140 | return query.ToString(); 141 | } 142 | } -------------------------------------------------------------------------------- /NetStone/Search/Character/SortKind.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.Search.Character; 2 | 3 | /// 4 | /// Result sorting schemes for character searches. 5 | /// 6 | public enum SortKind 7 | { 8 | /// 9 | /// Sort by name A to Z 10 | /// 11 | NameAtoZ = 1, 12 | 13 | /// 14 | /// Sort by name Z to A 15 | /// 16 | NameZtoA = 2, 17 | 18 | /// 19 | /// Sort by Homeworld A to Z 20 | /// 21 | WorldAtoZ = 3, 22 | 23 | /// 24 | /// Sort by Homeworld Z to A 25 | /// 26 | WorldZtoA = 4, 27 | 28 | /// 29 | /// Sort by level descending 30 | /// 31 | LevelDesc = 5, 32 | 33 | /// 34 | /// Sort by level ascending 35 | /// 36 | LevelAsc = 6, 37 | } -------------------------------------------------------------------------------- /NetStone/Search/FreeCompany/ActiveMembers.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.Search.FreeCompany; 2 | 3 | /// 4 | /// Active member filters for FC searches. 5 | /// 6 | public enum ActiveMembers 7 | { 8 | /// 9 | /// Include all FCs 10 | /// 11 | All, 12 | 13 | /// 14 | /// Filter for FCs with less than 10 members 15 | /// 16 | OneToTen, 17 | 18 | /// 19 | /// Filter for FCs with 11-30 members 20 | /// 21 | ElevenToThirty, 22 | 23 | /// 24 | /// Filter for FCs with 31-50 members 25 | /// 26 | ThirtyOneToFifty, 27 | 28 | /// 29 | /// Filter for FCs with more then 50 members 30 | /// 31 | OverFiftyOne, 32 | } -------------------------------------------------------------------------------- /NetStone/Search/FreeCompany/ActiveTimes.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.Search.FreeCompany; 2 | 3 | /// 4 | /// Active time filters for FC searches. 5 | /// 6 | public enum ActiveTimes 7 | { 8 | /// 9 | /// All options specified 10 | /// 11 | All, 12 | 13 | /// 14 | /// FC is active on week days 15 | /// 16 | WeekdaysOnly, 17 | 18 | /// 19 | /// FC is active on weekends 20 | /// 21 | WeekendsOnly, 22 | 23 | /// 24 | /// FC is always active 25 | /// 26 | Always, 27 | } -------------------------------------------------------------------------------- /NetStone/Search/FreeCompany/Focus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NetStone.Search.FreeCompany; 4 | 5 | /// 6 | /// Focus filters for FC searches. 7 | /// 8 | [Flags] 9 | public enum Focus 10 | { 11 | /// 12 | /// No focus specified 13 | /// 14 | NotSpecified = 0, 15 | 16 | /// 17 | /// Focus on Role Play 18 | /// 19 | RolePlaying = 1 << 0, 20 | 21 | /// 22 | /// Focus on leveling 23 | /// 24 | Leveling = 1 << 1, 25 | 26 | /// 27 | /// Focus on casual content 28 | /// 29 | Casual = 1 << 2, 30 | 31 | /// 32 | /// Focus on hardcore content 33 | /// 34 | Hardcore = 1 << 3, 35 | 36 | /// 37 | /// Focus on Dungeon content 38 | /// 39 | Dungeons = 1 << 4, 40 | 41 | /// 42 | /// Focus on guild heists 43 | /// 44 | Guildhests = 1 << 5, 45 | 46 | /// 47 | /// Focus on trials 48 | /// 49 | Trials = 1 << 6, 50 | 51 | /// 52 | /// Focus on raiding 53 | /// 54 | Raids = 1 << 7, 55 | 56 | /// 57 | /// Focus on PvP 58 | /// 59 | PvP = 1 << 8, 60 | } -------------------------------------------------------------------------------- /NetStone/Search/FreeCompany/FreeCompanySearchQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using NetStone.StaticData; 4 | 5 | namespace NetStone.Search.FreeCompany; 6 | 7 | /// 8 | /// Models a search query for Free companies 9 | /// 10 | public class FreeCompanySearchQuery : ISearchQuery 11 | { 12 | /// 13 | /// Name to search for 14 | /// 15 | public string Name { get; set; } = ""; 16 | 17 | /// 18 | /// World name the FC should belong to 19 | /// 20 | public string World { get; set; } = ""; 21 | 22 | /// 23 | /// Data center the FC should belong to 24 | /// 25 | public string DataCenter { get; set; } = ""; 26 | 27 | /// 28 | /// IF the FC is recruiting via Community Finder 29 | /// 30 | public bool IsCommunityFinderRecruiting { get; set; } 31 | 32 | /// 33 | /// Filter by active times 34 | /// 35 | public ActiveTimes ActiveTimes { get; set; } = ActiveTimes.All; 36 | 37 | /// 38 | /// Filter by number of active players 39 | /// 40 | public ActiveMembers ActiveMembers { get; set; } = ActiveMembers.All; 41 | 42 | /// 43 | /// Filter by recruitment status 44 | /// 45 | public Recruitment Recruitment { get; set; } = Recruitment.All; 46 | 47 | /// 48 | /// Filter by housing status 49 | /// 50 | public Housing Housing { get; set; } = Housing.All; 51 | 52 | /// 53 | /// Filter by content focus of the FC 54 | /// 55 | public Focus Focus { get; set; } 56 | 57 | /// 58 | /// Filter by type of players the FC is seeking 59 | /// 60 | public Seeking Seeking { get; set; } 61 | 62 | /// 63 | /// Filter for Grand Company membership 64 | /// 65 | public GrandCompany GrandCompany { get; set; } = GrandCompany.None; 66 | 67 | /// 68 | /// How to sort results 69 | /// 70 | public SortKind SortKind { get; set; } = SortKind.NameAtoZ; 71 | 72 | /// 73 | /// 74 | public string BuildQueryString() 75 | { 76 | if (!string.IsNullOrEmpty(this.World) && !string.IsNullOrEmpty(this.DataCenter)) 77 | throw new ArgumentException( 78 | "You cannot specify World and DataCenter at the same time in one search query."); 79 | 80 | var query = new StringBuilder($"?q={this.Name}"); 81 | 82 | if (this.IsCommunityFinderRecruiting) 83 | query.Append("&cf_public=1"); 84 | 85 | 86 | if (!string.IsNullOrEmpty(this.World)) 87 | query.Append($"&worldname={this.World}"); 88 | 89 | if (!string.IsNullOrEmpty(this.DataCenter)) 90 | query.Append($"&worldname=_dc_{this.DataCenter}"); 91 | 92 | 93 | if (this.ActiveTimes != ActiveTimes.All) 94 | query.Append($"&activetime={(int)this.ActiveTimes}"); 95 | 96 | 97 | if (this.Recruitment != Recruitment.All) 98 | query.Append($"&join={(int)this.Recruitment}"); 99 | 100 | 101 | if (this.Housing != Housing.All) 102 | query.Append($"&house={(int)this.Housing}"); 103 | 104 | 105 | if (this.ActiveMembers == ActiveMembers.OneToTen) 106 | query.Append("&character_count=1-10"); 107 | 108 | if (this.ActiveMembers == ActiveMembers.ElevenToThirty) 109 | query.Append("&character_count=11-30"); 110 | 111 | if (this.ActiveMembers == ActiveMembers.ThirtyOneToFifty) 112 | query.Append("&character_count=31-50"); 113 | 114 | if (this.ActiveMembers == ActiveMembers.OverFiftyOne) 115 | query.Append("&character_count=50-"); 116 | 117 | 118 | if (this.Focus.HasFlag(Focus.RolePlaying)) 119 | query.Append("&activities=0"); 120 | 121 | if (this.Focus.HasFlag(Focus.Leveling)) 122 | query.Append("&activities=1"); 123 | 124 | if (this.Focus.HasFlag(Focus.Casual)) 125 | query.Append("&activities=2"); 126 | 127 | if (this.Focus.HasFlag(Focus.Hardcore)) 128 | query.Append("&activities=3"); 129 | 130 | if (this.Focus.HasFlag(Focus.Dungeons)) 131 | query.Append("&activities=6"); 132 | 133 | if (this.Focus.HasFlag(Focus.Guildhests)) 134 | query.Append("&activities=4"); 135 | 136 | if (this.Focus.HasFlag(Focus.Trials)) 137 | query.Append("&activities=5"); 138 | 139 | if (this.Focus.HasFlag(Focus.Raids)) 140 | query.Append("&activities=7"); 141 | 142 | if (this.Focus.HasFlag(Focus.PvP)) 143 | query.Append("&activities=8"); 144 | 145 | 146 | if (this.Seeking.HasFlag(Seeking.Tank)) 147 | query.Append("&roles=16"); 148 | 149 | if (this.Seeking.HasFlag(Seeking.Healer)) 150 | query.Append("&roles=17"); 151 | 152 | if (this.Seeking.HasFlag(Seeking.Dps)) 153 | query.Append("&roles=18"); 154 | 155 | if (this.Seeking.HasFlag(Seeking.Crafter)) 156 | query.Append("&roles=19"); 157 | 158 | if (this.Seeking.HasFlag(Seeking.Gatherer)) 159 | query.Append("&roles=20"); 160 | 161 | 162 | if (this.GrandCompany.HasFlag(GrandCompany.Maelstrom)) 163 | query.Append("&gcid=1"); 164 | 165 | if (this.GrandCompany.HasFlag(GrandCompany.OrderOfTheTwinAdder)) 166 | query.Append("&gcid=2"); 167 | 168 | if (this.GrandCompany.HasFlag(GrandCompany.ImmortalFlames)) 169 | query.Append("&gcid=3"); 170 | 171 | 172 | query.Append($"&order={(int)this.SortKind}"); 173 | 174 | return query.ToString(); 175 | } 176 | } -------------------------------------------------------------------------------- /NetStone/Search/FreeCompany/Housing.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.Search.FreeCompany; 2 | 3 | /// 4 | /// Housing filters for FC searches. 5 | /// 6 | public enum Housing 7 | { 8 | /// 9 | /// Do not filter by housing 10 | /// 11 | All = 99, 12 | 13 | /// 14 | /// Filter for FCs with an estate built 15 | /// 16 | EstateBuilt = 2, 17 | 18 | /// 19 | /// Filter for FCs with a plot but no estate built 20 | /// 21 | PlotOnly = 1, 22 | 23 | /// 24 | /// Filter for Fcs with neither an estate nor a plot 25 | /// 26 | NoEstateOrPlot = 0, 27 | } -------------------------------------------------------------------------------- /NetStone/Search/FreeCompany/Recruitment.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.Search.FreeCompany; 2 | 3 | /// 4 | /// Recruitment filters for FC searches. 5 | /// 6 | public enum Recruitment 7 | { 8 | /// 9 | /// Dot not filter by recruitment status 10 | /// 11 | All = 99, 12 | 13 | /// 14 | /// Filter for groups actively recruiting 15 | /// 16 | Open = 1, 17 | 18 | /// 19 | /// Filter for groups not actively recruiting 20 | /// 21 | Closed = 0 22 | } -------------------------------------------------------------------------------- /NetStone/Search/FreeCompany/Seeking.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NetStone.Search.FreeCompany; 4 | 5 | /// 6 | /// Seeking player filters for FC searches. 7 | /// 8 | [Flags] 9 | public enum Seeking 10 | { 11 | /// 12 | /// No status specified 13 | /// 14 | NotSpecified = 0, 15 | 16 | /// 17 | /// Seeking tanks 18 | /// 19 | Tank = 1 << 0, 20 | 21 | /// 22 | /// Seeking healers 23 | /// 24 | Healer = 1 << 1, 25 | 26 | /// 27 | /// Seeking DPS 28 | /// 29 | Dps = 1 << 2, 30 | 31 | /// 32 | /// Seeking crafters 33 | /// 34 | Crafter = 1 << 3, 35 | 36 | /// 37 | /// Seeking gatherers 38 | /// 39 | Gatherer = 1 << 4, 40 | } -------------------------------------------------------------------------------- /NetStone/Search/FreeCompany/SortKind.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.Search.FreeCompany; 2 | 3 | /// 4 | /// Result sorting schemes for FC searches. 5 | /// 6 | public enum SortKind 7 | { 8 | /// 9 | /// Sort by name from A to Z 10 | /// 11 | NameAtoZ = 1, 12 | 13 | /// 14 | /// Sort by name from Z to A 15 | /// 16 | NameZtoA = 2, 17 | 18 | /// 19 | /// Sort by member count (high to low) 20 | /// 21 | MembershipHighToLow = 3, 22 | 23 | /// 24 | /// Sort by member count (low to high) 25 | /// 26 | MembershipLowToHigh = 4, 27 | 28 | /// 29 | /// Sort by foundation date (newest first) 30 | /// 31 | DateFoundedMostRecent = 5, 32 | 33 | /// 34 | /// Sort by foundation date (oldest first) 35 | /// 36 | DateFoundedOldest = 6, 37 | } -------------------------------------------------------------------------------- /NetStone/Search/ISearchQuery.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.Search; 2 | 3 | /// 4 | /// Models a search query for search requests 5 | /// 6 | public interface ISearchQuery 7 | { 8 | /// 9 | /// Constructs the query to send modeling this search query 10 | /// 11 | /// Search parameters to append to the request uri 12 | public string BuildQueryString(); 13 | } -------------------------------------------------------------------------------- /NetStone/Search/Linkshell/LinkshellSearchQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace NetStone.Search.Linkshell; 5 | 6 | /// 7 | /// Models a search for a link shell 8 | /// 9 | public class LinkshellSearchQuery : BaseLinkshellSearchQuery 10 | { 11 | 12 | /// 13 | /// Datacenter 14 | /// This is ignored if is set 15 | /// 16 | public string DataCenter { get; set; } = ""; 17 | 18 | /// 19 | /// Home-world 20 | /// 21 | public string HomeWorld { get; set; } = ""; 22 | /// 23 | public override string BuildQueryString() 24 | { 25 | if(string.IsNullOrEmpty(this.Name)) 26 | throw new ArgumentException("Name must not be empty or null.", nameof(this.Name)); 27 | 28 | var query = new StringBuilder(); 29 | 30 | query.Append($"?q={this.Name}"); 31 | if(this.RecruitingOnly) 32 | query.Append("&cf_public=1"); 33 | query.Append($"&worldname={(string.IsNullOrEmpty(this.HomeWorld) ? $"_dc_{this.DataCenter}" : this.HomeWorld)}"); 34 | query.Append($@"&character_count={this.ActiveMembers switch 35 | { 36 | LinkshellSizeCategory.OneToTen => "1-10", 37 | LinkshellSizeCategory.ElevenToThirty => "11-30", 38 | LinkshellSizeCategory.ThirtyOneToFifty => "31-51", 39 | LinkshellSizeCategory.OverFiftyOne => "51-", 40 | _ => "", 41 | }}"); 42 | query.Append($"&order={this.Sorting:D}"); 43 | 44 | return query.ToString(); 45 | } 46 | } 47 | 48 | /// 49 | /// Models a search for a cross world link shell 50 | /// 51 | public class CrossworldLinkshellSearchQuery : BaseLinkshellSearchQuery 52 | { 53 | 54 | /// 55 | /// Datacenter 56 | /// 57 | public string DataCenter { get; set; } = ""; 58 | /// 59 | public override string BuildQueryString() 60 | { 61 | if(string.IsNullOrEmpty(this.Name)) 62 | throw new ArgumentException("Name must not be empty or null.", nameof(this.Name)); 63 | 64 | var query = new StringBuilder(); 65 | 66 | query.Append($"?q={this.Name}"); 67 | if(this.RecruitingOnly) 68 | query.Append("&cf_public=1"); 69 | query.Append($"&dcname={this.DataCenter}"); 70 | query.Append($@"&character_count={this.ActiveMembers switch 71 | { 72 | LinkshellSizeCategory.OneToTen => "1-10", 73 | LinkshellSizeCategory.ElevenToThirty => "11-30", 74 | LinkshellSizeCategory.ThirtyOneToFifty => "31-51", 75 | LinkshellSizeCategory.OverFiftyOne => "51-", 76 | _ => "", 77 | }}"); 78 | query.Append($"&order={this.Sorting:D}"); 79 | 80 | return query.ToString(); 81 | } 82 | } 83 | 84 | /// 85 | /// Models a search for a cross world link shell 86 | /// 87 | public abstract class BaseLinkshellSearchQuery : ISearchQuery 88 | { 89 | /// 90 | /// Only search for actively recruiting 91 | /// 92 | public bool RecruitingOnly { get; set; } 93 | 94 | /// 95 | /// Name 96 | /// 97 | public string Name { get; set; } = ""; 98 | 99 | /// 100 | /// Active member count 101 | /// 102 | public LinkshellSizeCategory ActiveMembers { get; set; } = LinkshellSizeCategory.All; 103 | 104 | 105 | /// 106 | /// Sort order 107 | /// 108 | public LinkshellSortKind Sorting { get; set; } = LinkshellSortKind.CreationDateNewToOld; 109 | 110 | /// 111 | public abstract string BuildQueryString(); 112 | } 113 | 114 | /// 115 | /// Available choice for member count 116 | /// 117 | public enum LinkshellSizeCategory 118 | { 119 | /// 120 | /// All 121 | /// 122 | All, 123 | /// 124 | /// 1-10 125 | /// 126 | OneToTen, 127 | /// 128 | /// 11-30 129 | /// 130 | ElevenToThirty, 131 | /// 132 | /// 31-50 133 | /// 134 | ThirtyOneToFifty, 135 | /// 136 | /// Over 51 137 | /// 138 | OverFiftyOne, 139 | } 140 | 141 | /// 142 | /// Ways to sort linkshell and cwls search results 143 | /// 144 | public enum LinkshellSortKind 145 | { 146 | /// 147 | /// Creation date (newest to oldest) 148 | /// 149 | CreationDateNewToOld = 1, 150 | /// 151 | /// Creation date (oldest to newest) 152 | /// 153 | CreationDateOldToNew = 2, 154 | /// 155 | /// Name (A - Z) 156 | /// 157 | NameAtoZ = 3, 158 | /// 159 | /// Name (Z - A) 160 | /// 161 | NameZtoA = 4, 162 | /// 163 | /// Membership (high to low) 164 | /// 165 | MemberCountDesc = 5, 166 | /// 167 | /// Membership (low to high) 168 | /// 169 | MemberCountAsc = 6, 170 | 171 | 172 | } -------------------------------------------------------------------------------- /NetStone/StaticData/ClassJob.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.StaticData; 2 | 3 | /// 4 | /// The ClassJob IDs. 5 | /// 6 | public enum ClassJob 7 | { 8 | /// 9 | /// No job specified 10 | /// 11 | None, 12 | 13 | /// 14 | /// Gladiator class 15 | /// 16 | Gladiator = 1, 17 | 18 | /// 19 | /// Pugilist class 20 | /// 21 | Pugilist = 2, 22 | 23 | /// 24 | /// Marauder class 25 | /// 26 | Marauder = 3, 27 | 28 | /// 29 | /// Lancer class 30 | /// 31 | Lancer = 4, 32 | 33 | /// 34 | /// Archer class 35 | /// 36 | Archer = 5, 37 | 38 | /// 39 | /// Conjurer class 40 | /// 41 | Conjurer = 6, 42 | 43 | /// 44 | /// Thaumaturge class 45 | /// 46 | Thaumaturge = 7, 47 | 48 | /// 49 | /// Carpenter 50 | /// 51 | Carpenter = 8, 52 | 53 | /// 54 | /// Blacksmith 55 | /// 56 | Blacksmith = 9, 57 | 58 | /// 59 | /// Armorer 60 | /// 61 | Armorer = 10, 62 | 63 | /// 64 | /// Goldsmith 65 | /// 66 | Goldsmith = 11, 67 | 68 | /// 69 | /// Leatherworker 70 | /// 71 | Leatherworker = 12, 72 | 73 | /// 74 | /// Weaver 75 | /// 76 | Weaver = 13, 77 | 78 | /// 79 | /// Alchemist 80 | /// 81 | Alchemist = 14, 82 | 83 | /// 84 | /// Culinarian 85 | /// 86 | Culinarian = 15, 87 | 88 | /// 89 | /// Miner 90 | /// 91 | Miner = 16, 92 | 93 | /// 94 | /// Botanist 95 | /// 96 | Botanist = 17, 97 | 98 | /// 99 | /// Fisher 100 | /// 101 | Fisher = 18, 102 | 103 | /// 104 | /// Paladin 105 | /// 106 | Paladin = 19, 107 | 108 | /// 109 | /// Monk 110 | /// 111 | Monk = 20, 112 | 113 | /// 114 | /// Warrior 115 | /// 116 | Warrior = 21, 117 | 118 | /// 119 | /// Dragoon 120 | /// 121 | Dragoon = 22, 122 | 123 | /// 124 | /// Bard 125 | /// 126 | Bard = 23, 127 | 128 | /// 129 | /// White Mage 130 | /// 131 | WhiteMage = 24, 132 | 133 | /// 134 | /// Black Mage 135 | /// 136 | BlackMage = 25, 137 | 138 | /// 139 | /// Arcanist class 140 | /// 141 | Arcanist = 26, 142 | 143 | /// 144 | /// Summoner 145 | /// 146 | Summoner = 27, 147 | 148 | /// 149 | /// Scholar 150 | /// 151 | Scholar = 28, 152 | 153 | /// 154 | /// Rogue class 155 | /// 156 | Rogue = 29, 157 | 158 | /// 159 | /// Ninja 160 | /// 161 | Ninja = 30, 162 | 163 | /// 164 | /// Machinist 165 | /// 166 | Machinist = 31, 167 | 168 | /// 169 | /// Dark Knight 170 | /// 171 | DarkKnight = 32, 172 | 173 | /// 174 | /// Astrologian 175 | /// 176 | Astrologian = 33, 177 | 178 | /// 179 | /// Samurai 180 | /// 181 | Samurai = 34, 182 | 183 | /// 184 | /// Red Mage 185 | /// 186 | RedMage = 35, 187 | 188 | /// 189 | /// Blue Mage 190 | /// 191 | BlueMage = 36, 192 | 193 | /// 194 | /// Gunbreaker 195 | /// 196 | Gunbreaker = 37, 197 | 198 | /// 199 | /// Dancer 200 | /// 201 | Dancer = 38, 202 | 203 | /// 204 | /// Reaper 205 | /// 206 | Reaper = 39, 207 | 208 | /// 209 | /// Sage 210 | /// 211 | Sage = 40, 212 | 213 | /// 214 | /// Viper 215 | /// 216 | Viper = 41, 217 | 218 | /// 219 | /// Pictomancer 220 | /// 221 | Pictomancer = 42 222 | } -------------------------------------------------------------------------------- /NetStone/StaticData/GrandCompany.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NetStone.StaticData; 4 | 5 | /// 6 | /// The Grand Company IDs. 7 | /// 8 | [Flags] 9 | public enum GrandCompany 10 | { 11 | /// 12 | /// Unspecified 13 | /// 14 | None = 0, 15 | 16 | /// 17 | /// No affiliation with any grand company 18 | /// 19 | NoAffiliation = 1 << 0, 20 | 21 | /// 22 | /// Affiliated with Maelstrom 23 | /// 24 | Maelstrom = 1 << 1, 25 | 26 | /// 27 | /// Affiliated with The Twin Adder 28 | /// 29 | OrderOfTheTwinAdder = 1 << 2, 30 | 31 | /// 32 | /// Affiliated with the Immortal Flames 33 | /// 34 | ImmortalFlames = 1 << 3, 35 | } -------------------------------------------------------------------------------- /NetStone/StaticData/Language.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NetStone.StaticData; 4 | 5 | /// 6 | /// The supported Lodestone languages. 7 | /// 8 | [Flags] 9 | public enum Language 10 | { 11 | /// 12 | /// No language specified 13 | /// 14 | None = 0, 15 | 16 | /// 17 | /// Japanese 18 | /// 19 | Japanese = 1 << 0, 20 | 21 | /// 22 | /// English 23 | /// 24 | English = 1 << 1, 25 | 26 | /// 27 | /// German 28 | /// 29 | German = 1 << 2, 30 | 31 | /// 32 | /// French 33 | /// 34 | French = 1 << 3, 35 | } -------------------------------------------------------------------------------- /NetStone/StaticData/Race.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.StaticData; 2 | 3 | /// 4 | /// The possible game character races. 5 | /// 6 | public enum Race 7 | { 8 | /// 9 | /// No Race 10 | /// 11 | None, 12 | 13 | /// 14 | /// Hyur Race 15 | /// 16 | Hyur, 17 | 18 | /// 19 | /// Elezen Race 20 | /// 21 | Elezen, 22 | 23 | /// 24 | /// Lalafell race 25 | /// 26 | Lalafell, 27 | 28 | /// 29 | /// Miqo'te race 30 | /// 31 | Miqote, 32 | 33 | /// 34 | /// Roegadyn race 35 | /// 36 | Roegadyn, 37 | 38 | /// 39 | /// Au Ra race 40 | /// 41 | AuRa, 42 | 43 | /// 44 | /// Rothgar race 45 | /// 46 | Hrothgar, 47 | 48 | /// 49 | /// Viera race 50 | /// 51 | Viera, 52 | } -------------------------------------------------------------------------------- /NetStone/StaticData/Role.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.StaticData; 2 | 3 | /// 4 | /// The game combat roles. 5 | /// 6 | public enum Role 7 | { 8 | /// 9 | /// No role 10 | /// 11 | None, 12 | 13 | /// 14 | /// Tank role 15 | /// 16 | Tank, 17 | 18 | /// 19 | /// Healer role 20 | /// 21 | Healer, 22 | 23 | /// 24 | /// DPS role 25 | /// 26 | Dps, 27 | } -------------------------------------------------------------------------------- /NetStone/StaticData/Tribe.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone.StaticData; 2 | 3 | /// 4 | /// The possible game character tribes. 5 | /// 6 | public enum Tribe 7 | { 8 | /// 9 | /// No tribe 10 | /// 11 | None, 12 | 13 | /// 14 | /// Midlander Hyur 15 | /// 16 | Midlander, 17 | 18 | /// 19 | /// Highlander Hyur 20 | /// 21 | Highlander, 22 | 23 | /// 24 | /// Wildwood Elezen 25 | /// 26 | Wildwood, 27 | 28 | /// 29 | /// Duskwight Elezen 30 | /// 31 | Duskwight, 32 | 33 | /// 34 | /// Plainsfolk Lalafell 35 | /// 36 | Plainsfolk, 37 | 38 | /// 39 | /// Dunesfolk Lalafell 40 | /// 41 | Dunesfolk, 42 | 43 | /// 44 | /// Seeker of the Sun Miqo'te 45 | /// 46 | SeekeroftheSun, 47 | 48 | /// 49 | /// Keeper of the Moon Miqo'te 50 | /// 51 | KeeperoftheMoon, 52 | 53 | /// 54 | /// Seawolf Roegadyn 55 | /// 56 | SeaWolf, 57 | 58 | /// 59 | /// Hellsguard Roegadyn 60 | /// 61 | Hellsguard, 62 | 63 | /// 64 | /// Raen Au Ra 65 | /// 66 | Raen, 67 | 68 | /// 69 | /// Xaela Au Ra 70 | /// 71 | Xaela, 72 | 73 | /// 74 | /// Helion Hrothgar 75 | /// 76 | Helions, 77 | 78 | /// 79 | /// Hrothgar of The Lost 80 | /// 81 | TheLost, 82 | 83 | /// 84 | /// Rava Viera 85 | /// 86 | Rava, 87 | 88 | /// 89 | /// Veena Viera 90 | /// 91 | Veena, 92 | } -------------------------------------------------------------------------------- /NetStone/UserAgent.cs: -------------------------------------------------------------------------------- 1 | namespace NetStone; 2 | 3 | internal enum UserAgent 4 | { 5 | Desktop, 6 | Mobile, 7 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NetStone [![Nuget](https://img.shields.io/nuget/v/NetStone)](https://www.nuget.org/packages/NetStone) ![GitHub License](https://img.shields.io/github/license/xivapi/netstone) 2 | 3 | 4 | NetStone is a portable and modern .NET FFXIV Lodestone API. 5 | 6 | ## What works 7 | 8 | - [x] Characters 9 | - [x] Character Search 10 | - [x] FCs 11 | - [x] FC Search 12 | - [ ] PvP Teams 13 | - [ ] PvP Team Search 14 | - [x] Linkshell 15 | - [x] Linkshell Search 16 | - [x] CWLS 17 | - [x] CWLS Search 18 | 19 | Eorzea DB support is not planned. 20 | 21 | ## Usage 22 | 23 | ### Set up the client 24 | If you want to use NetStone you need to create one instance of the LodestoneClient and use this instance for all your requests. 25 | Note that this operation downloads current definitions and can therefore take an unknown amount of time or even throw and exception. 26 | 27 | #### Example Code 28 | 29 | ```C# 30 | try{ 31 | var lodestoneClient = await LodestoneClient.GetClientAsync(); 32 | } catch(HttpRequestException ex){ 33 | ... 34 | } 35 | ``` 36 | 37 | ### Retrieve character information 38 | Character information is fetched using the character's lodestone ID (the number contained in the Url). 39 | If the ID is not known to you, you can use the built in search functionality to look up a character by name and home world. 40 | If you need to fetch data for a specific character often it is best practice to save the Lodestone Id. 41 | Note that the search can have 0 results and that a character is null if the request failed. 42 | #### Example code 43 | ```C# 44 | try 45 | { 46 | //Get Lodestone Id if not known 47 | var searchResponse = await lodestoneClient.SearchCharacter(new CharacterSearchQuery() 48 | { 49 | CharacterName = "Name Surname", 50 | World = "Lich" 51 | }); 52 | var lodestoneCharacter = 53 | searchResponse?.Results 54 | .FirstOrDefault(entry => entry.Name == "Name Surname"); 55 | string lodestoneId = lodestoneCharacter.Id; 56 | 57 | //If Lodestone id is known 58 | var lodestoneCharacter = await lodestoneClient.GetCharacter(lodestoneId); 59 | } catch(HttpRequestException e) { 60 | //Handle potential errors in web request 61 | ... 62 | } 63 | ``` -------------------------------------------------------------------------------- /compile-fbs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo Clearing ./NetStone/GameData/Internal/... 4 | rm -rf ./NetStone/GameData/Internal/ 5 | 6 | echo Packing data exports... 7 | ./flatc --csharp -o ./NetStone.GameData.Packs/Internal/ --gen-onefile --filename-suffix "" ./lib/lodestone-data-exports/schema/*.fbs 8 | #go-bindata -o internal/pack/exports/gamedata.go -prefix "lodestone-data-exports/pack" -ignore="(LICENSE|README.md|.git|.gitignore|meta.json|LodestoneDataExporter.*|schema|.vscode)" lodestone-data-exports/... 9 | #sed -i "s/package main/package exports/g" internal/pack/exports/gamedata.go 10 | 11 | find -D exec ./NetStone.GameData.Packs/Internal/ -type f -exec sed -i 's/using global::Google.FlatBuffers;/using global::FlatBuffers;/g' {} \; 12 | #find -D exec ./NetStone.GameData.Packs/Internal/ -type f -exec sed -i 's/FFXIV/NetStone.GameData.Internal/g' {} \; 13 | 14 | echo Done! 15 | -------------------------------------------------------------------------------- /patch-flatbuffers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | sed -i 's/netstandard2.0;net46/netstandard2.0/g' ./lib/flatbuffers/net/FlatBuffers/FlatBuffers.csproj 4 | 5 | echo Done! 6 | --------------------------------------------------------------------------------