Skip to content

Commit

Permalink
Merge pull request #1 from digital-pet/Dev
Browse files Browse the repository at this point in the history
Merge Dev to main for 0.5.0 release
  • Loading branch information
digital-pet authored Dec 10, 2021
2 parents 81cde9b + 24b79fa commit d1ac9d4
Show file tree
Hide file tree
Showing 14 changed files with 876 additions and 250 deletions.
128 changes: 91 additions & 37 deletions AetherSenseRedux/Configuration.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using Dalamud.Configuration;
using AetherSenseRedux.Pattern;
using AetherSenseRedux.Trigger;
using Dalamud.Configuration;
using Dalamud.Logging;
using Dalamud.Plugin;
using System;
using System.Collections.Generic;
Expand All @@ -8,49 +11,17 @@ namespace AetherSenseRedux
[Serializable]
public class Configuration : IPluginConfiguration
{
public int Version { get; set; } = 0;
public int Version { get; set; } = 1;
public bool Initialized = false;

public bool SomePropertyToBeSavedAndWithADefault { get; set; } = true;
public bool LogChat { get; set; } = false;

public bool Enabled { get; set; } = false;
public string Address { get; set; } = "ws://127.0.0.1:12345";

public List<string> SeenDevices { get; set; } = new();

public List<Dictionary<string, dynamic>> Triggers { get; set; } = new List<Dictionary<string, dynamic>>
{
new Dictionary<string, dynamic>
{
{"type", "Chat" },
{ "name", "Cast" },
{ "enabledDevices", new List<string>()},
{ "pattern", "Constant" },
{ "patternSettings", new Dictionary<string, dynamic>
{
{"level", 1 },
{"duration", 250 }
}
},
{ "regex", "You cast" },
{ "retriggerDelay", 0 }
},
new Dictionary<string, dynamic>
{
{"type", "Chat" },
{ "name", "Casting" },
{ "enabledDevices", new List<string>()},
{ "pattern", "Ramp" },
{ "patternSettings", new Dictionary<string, dynamic>
{
{"start", 0 },
{"end", 0.75 },
{"duration", 2500 }
}
},
{ "regex", "You begin casting" },
{ "retriggerDelay", 250 }
}
};
public List<dynamic> Triggers { get; set; } = new List<dynamic>();

// the below exist just to make saving less cumbersome

Expand All @@ -62,9 +33,92 @@ public void Initialize(DalamudPluginInterface pluginInterface)
this.pluginInterface = pluginInterface;
}

public void FixDeserialization()
{
List<TriggerConfig> triggers = new List<TriggerConfig>();
foreach (dynamic t in Triggers)
{
triggers.Add(TriggerFactory.GetTriggerConfigFromObject(t));
}
Triggers = new List<dynamic>();

foreach (TriggerConfig t in triggers)
{
Triggers.Add(t);
}
}

public void LoadDefaults()
{
Version = 1;
Initialized = true;
Triggers = new List<dynamic>() {
new ChatTriggerConfig()
{
Name = "Casted",
EnabledDevices = new List<string>(),
Pattern = "Constant",
PatternSettings = new ConstantPatternConfig()
{
Level = 1,
Duration = 200
},
Regex = "You cast",
RetriggerDelay = 0
},
new ChatTriggerConfig()
{

Name = "Casting",
EnabledDevices = new List<string>(),
Pattern = "Ramp",
PatternSettings = new RampPatternConfig()
{
Start = 0,
End = 0.75,
Duration = 2500
},
Regex = "You begin casting",
RetriggerDelay = 250
}
};
}

public void Save()
{
pluginInterface!.SavePluginConfig(this);
}

public void Import(dynamic o)
{
try
{
if (o.Version != 1)
{
return;
}
Version = o.Version;
Initialized = o.Initialized;
LogChat = o.LogChat;
Address = o.Address;
SeenDevices = new List<string>(o.SeenDevices);
Triggers = o.Triggers;
FixDeserialization();
}
catch (Exception ex)
{
PluginLog.Error(ex, "Attempted to import a bad configuration.");
}
}
public Configuration CloneConfigurationFromDisk()
{
if (pluginInterface == null)
{
throw new NullReferenceException("Attempted to load the plugin configuration from a clone.");
}
var config = pluginInterface!.GetPluginConfig() as Configuration ?? throw new NullReferenceException("No configuration exists on disk.");
config.FixDeserialization();
return config;
}
}
}
54 changes: 40 additions & 14 deletions AetherSenseRedux/Device.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Threading.Tasks;
using AetherSenseRedux.Pattern;
using Buttplug;
using Dalamud.Logging;

namespace AetherSenseRedux
{
Expand All @@ -23,12 +24,17 @@ public Device(ButtplugClientDevice clientDevice)
_active = true;
}

public async Task Run()
public void Start()
{
Task.Run(MainLoop).ConfigureAwait(false);
}

public async Task MainLoop()
{
while (_active)
{
await OnTick();
await Task.Delay(10);
OnTick();
await Task.Yield();
}
}

Expand All @@ -37,35 +43,44 @@ public void Stop()
_active = false;
Patterns.Clear();

var t = Task.Run(() => WriteAsync(0));
var t = Task.Run(() => Write(0));
t.Wait();
}

private async Task OnTick()
private void OnTick()
{
List<double> intensities = new List<double>();
DateTime t = DateTime.UtcNow;
var patternsToRemove = new List<IPattern>();

foreach (var pattern in Patterns)
lock (Patterns)
{
try
foreach (var pattern in Patterns)
{
intensities.Add(pattern.GetIntensityAtTime(t));
try
{
intensities.Add(pattern.GetIntensityAtTime(t));
}
catch (PatternExpiredException)
{
patternsToRemove.Add(pattern);
}
}
catch (PatternExpiredException)
}
foreach (var pattern in patternsToRemove)
{
lock (Patterns)
{
// possible issue here depending on how C# compares objects, if so adding a guid to each pattern instance would resolve it
Patterns.Remove(pattern);
}
}

//TODO: Allow different merge modes besides average
double intensity = (intensities.Any()) ? intensities.Average() : 0;

await WriteAsync(intensity);
Write(intensity);
}

private async Task WriteAsync(double intensity)
private void Write(double intensity)
{
// clamp intensity before comparing to reduce unnecessary writes to device
double clampedIntensity = Clamp(intensity, 0, 1);
Expand All @@ -76,9 +91,20 @@ private async Task WriteAsync(double intensity)
}

_lastIntensity = clampedIntensity;

// If we don't wait on this, bad things happen on Linux and disappointing things happen on Windows, especially with slow BLE adapters.
try
{
var t = ClientDevice.SendVibrateCmd(clampedIntensity);
t.Wait();
} catch (Exception)
{
// Connecting to an intiface server on Linux will spam the log with bluez errors
// so we just ignore all exceptions from this statement. Good? Probably not. Necessary? Yes.
}

await ClientDevice.SendVibrateCmd(clampedIntensity);
}

private static double Clamp(double value, double min, double max)
{
return (value < min) ? min : (value > max) ? max : value;
Expand Down
20 changes: 11 additions & 9 deletions AetherSenseRedux/Pattern/ConstantPattern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ internal class ConstantPattern : IPattern
public DateTime Expires { get; set; }
private readonly double level;

public ConstantPattern(Dictionary<string, dynamic> config)
public ConstantPattern(ConstantPatternConfig config)
{
level = (double)config["level"];
Expires = DateTime.UtcNow + TimeSpan.FromMilliseconds((long)config["duration"]);
level = config.Level;
Expires = DateTime.UtcNow + TimeSpan.FromMilliseconds(config.Duration);
}

public double GetIntensityAtTime(DateTime time)
Expand All @@ -23,13 +23,15 @@ public double GetIntensityAtTime(DateTime time)
}
return level;
}
public static Dictionary<string, dynamic> GetDefaultConfiguration()
public static PatternConfig GetDefaultConfiguration()
{
return new Dictionary<string, dynamic>
{
{"level", 1 },
{"duration", 1000 }
};
return new ConstantPatternConfig();
}
}
[Serializable]
public class ConstantPatternConfig : PatternConfig
{
public override string Type { get; } = "Constant";
public double Level { get; set; } = 1;
}
}
10 changes: 8 additions & 2 deletions AetherSenseRedux/Pattern/IPattern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ internal interface IPattern
DateTime Expires { get; set; }
double GetIntensityAtTime(DateTime currTime);

static Dictionary<string, dynamic> GetDefaultConfiguration()
static PatternConfig GetDefaultConfiguration()
{
return new Dictionary<string, dynamic>();
throw new NotImplementedException();
}

}
[Serializable]
public abstract class PatternConfig
{
public abstract string Type { get; }
public long Duration { get; set; } = 1000;
}
}
55 changes: 47 additions & 8 deletions AetherSenseRedux/Pattern/PatternFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@ namespace AetherSenseRedux.Pattern
{
internal class PatternFactory
{
public static IPattern GetPatternFromString(string name, Dictionary<string, dynamic> settings)
public static IPattern GetPatternFromObject(PatternConfig settings)
{
switch (name)
switch (settings.Type)
{
case "Constant":
return new ConstantPattern(settings);
return new ConstantPattern((ConstantPatternConfig)settings);
case "Ramp":
return new RampPattern(settings);
return new RampPattern((RampPatternConfig)settings);
case "Random":
return new RandomPattern(settings);
return new RandomPattern((RandomPatternConfig)settings);
case "Square":
return new SquarePattern(settings);
return new SquarePattern((SquarePatternConfig)settings);
default:
throw new ArgumentException(String.Format("Invalid pattern {0} specified",name));
throw new ArgumentException(String.Format("Invalid pattern {0} specified", settings.Type));
}
}

public static Dictionary<string,dynamic> GetDefaultsFromString(string name)
public static PatternConfig GetDefaultsFromString(string name)
{
switch (name)
{
Expand All @@ -41,5 +41,44 @@ public static Dictionary<string,dynamic> GetDefaultsFromString(string name)
throw new ArgumentException(String.Format("Invalid pattern {0} specified", name));
}
}

public static PatternConfig GetPatternConfigFromObject(dynamic o)
{
switch ((string)o.Type)
{
case "Constant":
return new ConstantPatternConfig()
{
Duration = (long)o.Duration,
Level = (double)o.Level
};
case "Ramp":
return new RampPatternConfig()
{
Duration = (long)o.Duration,
Start = (double)o.Start,
End = (double)o.End
};
case "Random":
return new RandomPatternConfig()
{
Duration = (long)o.Duration,
Minimum = (double)o.Minimum,
Maximum = (double)o.Maximum
};
case "Square":
return new SquarePatternConfig()
{
Duration = (long)o.Duration,
Duration1 = (long)o.Duration1,
Duration2 = (long)o.Duration2,
Level1 = (double)o.Level1,
Level2 = (double)o.Level2,
Offset = (long)o.Offset
};
default:
throw new ArgumentException(String.Format("Invalid pattern {0} specified", o.Type));
}
}
}
}
Loading

0 comments on commit d1ac9d4

Please sign in to comment.