Compare commits
4 commits
d722deeefc
...
88600db779
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88600db779 | ||
|
|
1fdae96262 | ||
|
|
655bfd5163 | ||
|
|
84462d7c92 |
12 changed files with 425 additions and 29 deletions
309
MosswartMassacre/CharacterStats.cs
Normal file
309
MosswartMassacre/CharacterStats.cs
Normal file
|
|
@ -0,0 +1,309 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Decal.Adapter;
|
||||||
|
using Decal.Adapter.Wrappers;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace MosswartMassacre
|
||||||
|
{
|
||||||
|
public struct AllegianceInfoRecord
|
||||||
|
{
|
||||||
|
public string name;
|
||||||
|
public int rank;
|
||||||
|
public int race;
|
||||||
|
public int gender;
|
||||||
|
|
||||||
|
public AllegianceInfoRecord(string _name, int _rank, int _race, int _gender)
|
||||||
|
{
|
||||||
|
name = _name;
|
||||||
|
rank = _rank;
|
||||||
|
race = _race;
|
||||||
|
gender = _gender;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CharacterStats
|
||||||
|
{
|
||||||
|
// Cached allegiance data (populated from network messages)
|
||||||
|
private static string allegianceName;
|
||||||
|
private static int allegianceSize;
|
||||||
|
private static int followers;
|
||||||
|
private static AllegianceInfoRecord monarch;
|
||||||
|
private static AllegianceInfoRecord patron;
|
||||||
|
private static int allegianceRank;
|
||||||
|
|
||||||
|
// Cached luminance data (populated from network messages)
|
||||||
|
private static long luminanceEarned = -1;
|
||||||
|
private static long luminanceTotal = -1;
|
||||||
|
|
||||||
|
// Cached title data (populated from network messages)
|
||||||
|
private static int currentTitle = -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reset all cached data. Call on plugin init.
|
||||||
|
/// </summary>
|
||||||
|
internal static void Init()
|
||||||
|
{
|
||||||
|
allegianceName = null;
|
||||||
|
allegianceSize = 0;
|
||||||
|
followers = 0;
|
||||||
|
monarch = new AllegianceInfoRecord();
|
||||||
|
patron = new AllegianceInfoRecord();
|
||||||
|
allegianceRank = 0;
|
||||||
|
luminanceEarned = -1;
|
||||||
|
luminanceTotal = -1;
|
||||||
|
currentTitle = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Process game event 0x0020 - Allegiance info.
|
||||||
|
/// Extracts monarch, patron, rank, followers from the allegiance tree.
|
||||||
|
/// Reference: TreeStats Character.cs:642-745
|
||||||
|
/// </summary>
|
||||||
|
internal static void ProcessAllegianceInfoMessage(NetworkMessageEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
allegianceName = e.Message.Value<string>("allegianceName");
|
||||||
|
allegianceSize = e.Message.Value<Int32>("allegianceSize");
|
||||||
|
followers = e.Message.Value<Int32>("followers");
|
||||||
|
|
||||||
|
monarch = new AllegianceInfoRecord();
|
||||||
|
patron = new AllegianceInfoRecord();
|
||||||
|
|
||||||
|
MessageStruct records = e.Message.Struct("records");
|
||||||
|
int currentId = CoreManager.Current.CharacterFilter.Id;
|
||||||
|
var parentMap = new Dictionary<int, int>();
|
||||||
|
var recordMap = new Dictionary<int, AllegianceInfoRecord>();
|
||||||
|
|
||||||
|
for (int i = 0; i < records.Count; i++)
|
||||||
|
{
|
||||||
|
var record = records.Struct(i);
|
||||||
|
int charId = record.Value<int>("character");
|
||||||
|
int treeParent = record.Value<int>("treeParent");
|
||||||
|
|
||||||
|
parentMap[charId] = treeParent;
|
||||||
|
recordMap[charId] = new AllegianceInfoRecord(
|
||||||
|
record.Value<string>("name"),
|
||||||
|
record.Value<int>("rank"),
|
||||||
|
record.Value<int>("race"),
|
||||||
|
record.Value<int>("gender"));
|
||||||
|
|
||||||
|
// Monarch: treeParent <= 1
|
||||||
|
if (treeParent <= 1)
|
||||||
|
{
|
||||||
|
monarch = recordMap[charId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patron: parent of current character
|
||||||
|
if (parentMap.ContainsKey(currentId) && recordMap.ContainsKey(parentMap[currentId]))
|
||||||
|
{
|
||||||
|
patron = recordMap[parentMap[currentId]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our rank from the record
|
||||||
|
if (recordMap.ContainsKey(currentId))
|
||||||
|
{
|
||||||
|
allegianceRank = recordMap[currentId].rank;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"[CharStats] Allegiance processing error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Process game event 0x0013 - Character property data.
|
||||||
|
/// Extracts luminance from QWORD keys 6 and 7.
|
||||||
|
/// Reference: TreeStats Character.cs:582-640
|
||||||
|
/// </summary>
|
||||||
|
internal static void ProcessCharacterPropertyData(NetworkMessageEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MessageStruct props = e.Message.Struct("properties");
|
||||||
|
MessageStruct qwords = props.Struct("qwords");
|
||||||
|
|
||||||
|
for (int i = 0; i < qwords.Count; i++)
|
||||||
|
{
|
||||||
|
var tmpStruct = qwords.Struct(i);
|
||||||
|
long key = tmpStruct.Value<Int64>("key");
|
||||||
|
long value = tmpStruct.Value<Int64>("value");
|
||||||
|
|
||||||
|
if (key == 6) // AvailableLuminance
|
||||||
|
luminanceEarned = value;
|
||||||
|
else if (key == 7) // MaximumLuminance
|
||||||
|
luminanceTotal = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"[CharStats] Property processing error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Process game event 0x0029 - Titles list.
|
||||||
|
/// Extracts current title ID.
|
||||||
|
/// Reference: TreeStats Character.cs:551-580
|
||||||
|
/// </summary>
|
||||||
|
internal static void ProcessTitlesMessage(NetworkMessageEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
currentTitle = e.Message.Value<Int32>("current");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"[CharStats] Title processing error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Process game event 0x002b - Set title (when player changes title).
|
||||||
|
/// </summary>
|
||||||
|
internal static void ProcessSetTitleMessage(NetworkMessageEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
currentTitle = e.Message.Value<Int32>("title");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"[CharStats] Set title error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collect all character data and send via WebSocket.
|
||||||
|
/// Called on login (after delay) and every 10 minutes.
|
||||||
|
/// </summary>
|
||||||
|
internal static void CollectAndSend()
|
||||||
|
{
|
||||||
|
if (!PluginCore.WebSocketEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cf = CoreManager.Current.CharacterFilter;
|
||||||
|
var culture = new CultureInfo("en-US");
|
||||||
|
|
||||||
|
// --- Attributes ---
|
||||||
|
var attributes = new Dictionary<string, object>();
|
||||||
|
foreach (var attr in cf.Attributes)
|
||||||
|
{
|
||||||
|
attributes[attr.Name.ToLower()] = new
|
||||||
|
{
|
||||||
|
@base = attr.Base,
|
||||||
|
creation = attr.Creation
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Vitals (base values) ---
|
||||||
|
var vitals = new Dictionary<string, object>();
|
||||||
|
foreach (var vital in cf.Vitals)
|
||||||
|
{
|
||||||
|
vitals[vital.Name.ToLower()] = new
|
||||||
|
{
|
||||||
|
@base = vital.Base
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Skills ---
|
||||||
|
var skills = new Dictionary<string, object>();
|
||||||
|
Decal.Filters.FileService fs = CoreManager.Current.FileService as Decal.Filters.FileService;
|
||||||
|
if (fs != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < fs.SkillTable.Length; i++)
|
||||||
|
{
|
||||||
|
Decal.Interop.Filters.SkillInfo skillinfo = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
skillinfo = cf.Underlying.get_Skill(
|
||||||
|
(Decal.Interop.Filters.eSkillID)fs.SkillTable[i].Id);
|
||||||
|
|
||||||
|
string name = skillinfo.Name.ToLower().Replace(" ", "_");
|
||||||
|
string training = skillinfo.Training.ToString();
|
||||||
|
// Training enum returns "eTrainSpecialized" etc, strip "eTrain" prefix
|
||||||
|
if (training.Length > 6)
|
||||||
|
training = training.Substring(6);
|
||||||
|
|
||||||
|
skills[name] = new
|
||||||
|
{
|
||||||
|
@base = skillinfo.Base,
|
||||||
|
training = training
|
||||||
|
};
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (skillinfo != null)
|
||||||
|
{
|
||||||
|
Marshal.ReleaseComObject(skillinfo);
|
||||||
|
skillinfo = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Allegiance ---
|
||||||
|
object allegiance = null;
|
||||||
|
if (allegianceName != null)
|
||||||
|
{
|
||||||
|
allegiance = new
|
||||||
|
{
|
||||||
|
name = allegianceName,
|
||||||
|
monarch = monarch.name != null ? new
|
||||||
|
{
|
||||||
|
name = monarch.name,
|
||||||
|
race = monarch.race,
|
||||||
|
rank = monarch.rank,
|
||||||
|
gender = monarch.gender
|
||||||
|
} : null,
|
||||||
|
patron = patron.name != null ? new
|
||||||
|
{
|
||||||
|
name = patron.name,
|
||||||
|
race = patron.race,
|
||||||
|
rank = patron.rank,
|
||||||
|
gender = patron.gender
|
||||||
|
} : null,
|
||||||
|
rank = allegianceRank,
|
||||||
|
followers = followers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Build payload ---
|
||||||
|
var payload = new
|
||||||
|
{
|
||||||
|
type = "character_stats",
|
||||||
|
timestamp = DateTime.UtcNow.ToString("o"),
|
||||||
|
character_name = cf.Name,
|
||||||
|
level = cf.Level,
|
||||||
|
race = cf.Race,
|
||||||
|
gender = cf.Gender,
|
||||||
|
birth = cf.Birth.ToString(culture),
|
||||||
|
total_xp = cf.TotalXP,
|
||||||
|
unassigned_xp = cf.UnassignedXP,
|
||||||
|
skill_credits = cf.SkillPoints,
|
||||||
|
deaths = cf.Deaths,
|
||||||
|
luminance_earned = luminanceEarned >= 0 ? (long?)luminanceEarned : null,
|
||||||
|
luminance_total = luminanceTotal >= 0 ? (long?)luminanceTotal : null,
|
||||||
|
current_title = currentTitle >= 0 ? (int?)currentTitle : null,
|
||||||
|
attributes = attributes,
|
||||||
|
vitals = vitals,
|
||||||
|
skills = skills,
|
||||||
|
allegiance = allegiance
|
||||||
|
};
|
||||||
|
|
||||||
|
_ = WebSocket.SendCharacterStatsAsync(payload);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"[CharStats] Error collecting stats: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -35,10 +35,12 @@
|
||||||
<DefineConstants>TRACE;VVS_REFERENCED;DECAL_INTEROP</DefineConstants>
|
<DefineConstants>TRACE;VVS_REFERENCED;DECAL_INTEROP</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="0Harmony">
|
<Reference Include="0Harmony">
|
||||||
<HintPath>..\..\..\..\Documents\Decal Plugins\UtilityBelt\0Harmony.dll</HintPath>
|
<HintPath>lib\0Harmony.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Costura, Version=5.7.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="Costura, Version=5.7.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
|
@ -49,18 +51,18 @@
|
||||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Decal.FileService">
|
<Reference Include="Decal.FileService">
|
||||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Decal 3.0\Decal.FileService.dll</HintPath>
|
<HintPath>lib\Decal.FileService.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Decal.Interop.Core, Version=2.9.8.3, Culture=neutral, PublicKeyToken=481f17d392f1fb65, processorArchitecture=MSIL">
|
<Reference Include="Decal.Interop.Core">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Decal 3.0\.NET 4.0 PIA\Decal.Interop.Core.DLL</HintPath>
|
<HintPath>lib\Decal.Interop.Core.DLL</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Decal.Interop.Filters, Version=2.9.8.3, Culture=neutral, PublicKeyToken=481f17d392f1fb65, processorArchitecture=MSIL">
|
<Reference Include="Decal.Interop.Filters">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Decal 3.0\.NET 4.0 PIA\Decal.Interop.Filters.DLL</HintPath>
|
<HintPath>lib\Decal.Interop.Filters.DLL</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Decal.Interop.Inject, Version=2.9.8.3, Culture=neutral, PublicKeyToken=481f17d392f1fb65, processorArchitecture=MSIL">
|
<Reference Include="Decal.Interop.Inject, Version=2.9.8.3, Culture=neutral, PublicKeyToken=481f17d392f1fb65, processorArchitecture=MSIL">
|
||||||
|
|
@ -68,16 +70,16 @@
|
||||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||||
<HintPath>lib\Decal.Interop.Inject.dll</HintPath>
|
<HintPath>lib\Decal.Interop.Inject.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Decal.Interop.D3DService, Version=2.9.8.3, Culture=neutral, PublicKeyToken=481f17d392f1fb65, processorArchitecture=MSIL">
|
<Reference Include="Decal.Interop.D3DService">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Decal 3.0\.NET 4.0 PIA\Decal.Interop.D3DService.DLL</HintPath>
|
<HintPath>lib\Decal.Interop.D3DService.DLL</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Decal.Interop.Input">
|
<Reference Include="Decal.Interop.Input">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Decal 3.0\.NET 4.0 PIA\Decal.Interop.Input.DLL</HintPath>
|
<HintPath>lib\Decal.Interop.Input.DLL</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
<Reference Include="Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
|
@ -224,12 +226,12 @@
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="utank2-i, Version=1.0.0.0, Culture=neutral, processorArchitecture=x86">
|
<Reference Include="utank2-i">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>bin\Debug\utank2-i.dll</HintPath>
|
<HintPath>lib\utank2-i.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="VCS5">
|
<Reference Include="VCS5">
|
||||||
<HintPath>..\..\..\..\..\..\Games\Decal Plugins\Virindi\VirindiChatSystem5\VCS5.dll</HintPath>
|
<HintPath>lib\VCS5.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="VirindiViewService">
|
<Reference Include="VirindiViewService">
|
||||||
<HintPath>lib\VirindiViewService.dll</HintPath>
|
<HintPath>lib\VirindiViewService.dll</HintPath>
|
||||||
|
|
@ -333,6 +335,7 @@
|
||||||
<Compile Include="Views\FlagTrackerView.cs" />
|
<Compile Include="Views\FlagTrackerView.cs" />
|
||||||
<Compile Include="Views\VVSBaseView.cs" />
|
<Compile Include="Views\VVSBaseView.cs" />
|
||||||
<Compile Include="Views\VVSTabbedMainView.cs" />
|
<Compile Include="Views\VVSTabbedMainView.cs" />
|
||||||
|
<Compile Include="CharacterStats.cs" />
|
||||||
<Compile Include="WebSocket.cs" />
|
<Compile Include="WebSocket.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
@ -354,24 +357,17 @@
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<COMReference Include="Decal">
|
<Reference Include="Decal">
|
||||||
<Guid>{FF7F5F6D-34E0-4B6F-B3BB-8141DE2EF732}</Guid>
|
<HintPath>lib\Decal.dll</HintPath>
|
||||||
<VersionMajor>2</VersionMajor>
|
|
||||||
<VersionMinor>0</VersionMinor>
|
|
||||||
<Lcid>0</Lcid>
|
|
||||||
<WrapperTool>primary</WrapperTool>
|
|
||||||
<Isolated>False</Isolated>
|
|
||||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||||
</COMReference>
|
</Reference>
|
||||||
<COMReference Include="DecalNet">
|
<Reference Include="DecalNet">
|
||||||
<Guid>{572B87C4-93BD-46B3-A291-CD58181D25DC}</Guid>
|
<HintPath>lib\decalnet.dll</HintPath>
|
||||||
<VersionMajor>2</VersionMajor>
|
|
||||||
<VersionMinor>0</VersionMinor>
|
|
||||||
<Lcid>0</Lcid>
|
|
||||||
<WrapperTool>primary</WrapperTool>
|
|
||||||
<Isolated>False</Isolated>
|
|
||||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||||
</COMReference>
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<Import Project="..\packages\Fody.6.9.3\build\Fody.targets" Condition="Exists('..\packages\Fody.6.9.3\build\Fody.targets')" />
|
<Import Project="..\packages\Fody.6.9.3\build\Fody.targets" Condition="Exists('..\packages\Fody.6.9.3\build\Fody.targets')" />
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ namespace MosswartMassacre
|
||||||
internal static Timer updateTimer;
|
internal static Timer updateTimer;
|
||||||
private static Timer vitalsTimer;
|
private static Timer vitalsTimer;
|
||||||
private static System.Windows.Forms.Timer commandTimer;
|
private static System.Windows.Forms.Timer commandTimer;
|
||||||
|
private static Timer characterStatsTimer;
|
||||||
private static readonly Queue<string> pendingCommands = new Queue<string>();
|
private static readonly Queue<string> pendingCommands = new Queue<string>();
|
||||||
public static bool RareMetaEnabled { get; set; } = true;
|
public static bool RareMetaEnabled { get; set; } = true;
|
||||||
|
|
||||||
|
|
@ -182,6 +183,8 @@ namespace MosswartMassacre
|
||||||
CoreManager.Current.WorldFilter.CreateObject += OnInventoryCreate;
|
CoreManager.Current.WorldFilter.CreateObject += OnInventoryCreate;
|
||||||
CoreManager.Current.WorldFilter.ReleaseObject += OnInventoryRelease;
|
CoreManager.Current.WorldFilter.ReleaseObject += OnInventoryRelease;
|
||||||
CoreManager.Current.WorldFilter.ChangeObject += OnInventoryChange;
|
CoreManager.Current.WorldFilter.ChangeObject += OnInventoryChange;
|
||||||
|
// Subscribe to server messages for allegiance/luminance/title data
|
||||||
|
CoreManager.Current.EchoFilter.ServerDispatch += EchoFilter_ServerDispatch;
|
||||||
// Initialize VVS view after character login
|
// Initialize VVS view after character login
|
||||||
ViewManager.ViewInit();
|
ViewManager.ViewInit();
|
||||||
|
|
||||||
|
|
@ -251,7 +254,8 @@ namespace MosswartMassacre
|
||||||
CoreManager.Current.WorldFilter.CreateObject -= OnInventoryCreate;
|
CoreManager.Current.WorldFilter.CreateObject -= OnInventoryCreate;
|
||||||
CoreManager.Current.WorldFilter.ReleaseObject -= OnInventoryRelease;
|
CoreManager.Current.WorldFilter.ReleaseObject -= OnInventoryRelease;
|
||||||
CoreManager.Current.WorldFilter.ChangeObject -= OnInventoryChange;
|
CoreManager.Current.WorldFilter.ChangeObject -= OnInventoryChange;
|
||||||
|
// Unsubscribe from server dispatch
|
||||||
|
CoreManager.Current.EchoFilter.ServerDispatch -= EchoFilter_ServerDispatch;
|
||||||
|
|
||||||
// Stop and dispose of the timers
|
// Stop and dispose of the timers
|
||||||
if (updateTimer != null)
|
if (updateTimer != null)
|
||||||
|
|
@ -284,6 +288,15 @@ namespace MosswartMassacre
|
||||||
questStreamingTimer = null;
|
questStreamingTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop and dispose character stats timer
|
||||||
|
if (characterStatsTimer != null)
|
||||||
|
{
|
||||||
|
characterStatsTimer.Stop();
|
||||||
|
characterStatsTimer.Elapsed -= OnCharacterStatsUpdate;
|
||||||
|
characterStatsTimer.Dispose();
|
||||||
|
characterStatsTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Dispose quest manager
|
// Dispose quest manager
|
||||||
if (questManager != null)
|
if (questManager != null)
|
||||||
{
|
{
|
||||||
|
|
@ -403,6 +416,34 @@ namespace MosswartMassacre
|
||||||
WriteToChat($"[ERROR] Quest streaming initialization failed: {ex.Message}");
|
WriteToChat($"[ERROR] Quest streaming initialization failed: {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize character stats streaming
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CharacterStats.Init();
|
||||||
|
|
||||||
|
// Start 10-minute character stats timer
|
||||||
|
characterStatsTimer = new Timer(600000); // 10 minutes
|
||||||
|
characterStatsTimer.Elapsed += OnCharacterStatsUpdate;
|
||||||
|
characterStatsTimer.AutoReset = true;
|
||||||
|
characterStatsTimer.Start();
|
||||||
|
|
||||||
|
// Send initial stats after 5-second delay (let CharacterFilter populate)
|
||||||
|
var initialDelay = new Timer(5000);
|
||||||
|
initialDelay.AutoReset = false;
|
||||||
|
initialDelay.Elapsed += (s, args) =>
|
||||||
|
{
|
||||||
|
CharacterStats.CollectAndSend();
|
||||||
|
((Timer)s).Dispose();
|
||||||
|
};
|
||||||
|
initialDelay.Start();
|
||||||
|
|
||||||
|
WriteToChat("[OK] Character stats streaming initialized (10-min interval)");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
WriteToChat($"[ERROR] Character stats initialization failed: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Quest Streaming Methods
|
#region Quest Streaming Methods
|
||||||
|
|
@ -1161,6 +1202,50 @@ namespace MosswartMassacre
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void OnCharacterStatsUpdate(object sender, ElapsedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CharacterStats.CollectAndSend();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
WriteToChat($"[CharStats] Timer error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EchoFilter_ServerDispatch(object sender, NetworkMessageEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (e.Message.Type == 0xF7B0) // Game Event
|
||||||
|
{
|
||||||
|
int eventId = (int)e.Message["event"];
|
||||||
|
|
||||||
|
if (eventId == 0x0020) // Allegiance info
|
||||||
|
{
|
||||||
|
CharacterStats.ProcessAllegianceInfoMessage(e);
|
||||||
|
}
|
||||||
|
else if (eventId == 0x0013) // Login Character (properties)
|
||||||
|
{
|
||||||
|
CharacterStats.ProcessCharacterPropertyData(e);
|
||||||
|
}
|
||||||
|
else if (eventId == 0x0029) // Titles list
|
||||||
|
{
|
||||||
|
CharacterStats.ProcessTitlesMessage(e);
|
||||||
|
}
|
||||||
|
else if (eventId == 0x002b) // Set title
|
||||||
|
{
|
||||||
|
CharacterStats.ProcessSetTitleMessage(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
WriteToChat($"[CharStats] ServerDispatch error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void CalculateKillsPerInterval()
|
private void CalculateKillsPerInterval()
|
||||||
{
|
{
|
||||||
double minutesElapsed = (DateTime.Now - statsStartTime).TotalMinutes;
|
double minutesElapsed = (DateTime.Now - statsStartTime).TotalMinutes;
|
||||||
|
|
|
||||||
|
|
@ -296,6 +296,12 @@ namespace MosswartMassacre
|
||||||
await SendEncodedAsync(json, CancellationToken.None);
|
await SendEncodedAsync(json, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task SendCharacterStatsAsync(object statsData)
|
||||||
|
{
|
||||||
|
var json = JsonConvert.SerializeObject(statsData);
|
||||||
|
await SendEncodedAsync(json, CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task SendQuestDataAsync(string questName, string countdown)
|
public static async Task SendQuestDataAsync(string questName, string countdown)
|
||||||
{
|
{
|
||||||
var envelope = new
|
var envelope = new
|
||||||
|
|
|
||||||
Binary file not shown.
BIN
MosswartMassacre/lib/Decal.FileService.dll
Normal file
BIN
MosswartMassacre/lib/Decal.FileService.dll
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/Decal.Interop.D3DService.DLL
Normal file
BIN
MosswartMassacre/lib/Decal.Interop.D3DService.DLL
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/Decal.Interop.Filters.DLL
Normal file
BIN
MosswartMassacre/lib/Decal.Interop.Filters.DLL
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/Decal.Interop.Input.DLL
Normal file
BIN
MosswartMassacre/lib/Decal.Interop.Input.DLL
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/Decal.dll
Normal file
BIN
MosswartMassacre/lib/Decal.dll
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/VCS5.dll
Normal file
BIN
MosswartMassacre/lib/VCS5.dll
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/decalnet.dll
Normal file
BIN
MosswartMassacre/lib/decalnet.dll
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue