Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
kinsi55 committed Aug 21, 2021
0 parents commit 1535521
Show file tree
Hide file tree
Showing 15 changed files with 991 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

bin/*
obj/*
*.user
*.xcf
HarmonyPatches/RemoveSplashscreen.cs
HarmonyPatchHelper.cs
Screenshots/SocialPreview.jpg
42 changes: 42 additions & 0 deletions Configuration/Configuration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

using System.Runtime.CompilerServices;
using IPA.Config.Stores;

[assembly: InternalsVisibleTo(GeneratedStore.AssemblyVisibilityTarget)]
namespace OverswingCounter {
internal class Configuration {
public static Configuration Instance { get; set; }

public virtual int averageCount { get; set; } = 12;
public virtual int decimalPlaces { get; set; } = 0;
public virtual bool underswingByPoints { get; set; } = true;

public virtual float ignoreCutsWithNoPrecedingWithin { get; set; } = 0.6f;

public virtual float targetExtraAngle { get; set; } = 13f;
public virtual float lowerWarning { get; set; } = 8f;
public virtual float upperWarning { get; set; } = 13f;


/// <summary>
/// This is called whenever BSIPA reads the config from disk (including when file changes are detected).
/// </summary>
public virtual void OnReload() {
// Do stuff after config is read from disk.
}

/// <summary>
/// Call this to force BSIPA to update the config file. This is also called by BSIPA if it detects the file was modified.
/// </summary>
public virtual void Changed() {
// Do stuff when the config is changed.
}

/// <summary>
/// Call this to have BSIPA copy the values from <paramref name="other"/> into this config.
/// </summary>
public virtual void CopyFrom(Configuration other) {
// This instance's members populated from other
}
}
}
101 changes: 101 additions & 0 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This file contains the build tasks and targets for verifying the manifest, zipping Release builds,
and copying the plugin to to your Beat Saber folder. Only edit this if you know what you are doing. -->
<Project>
<PropertyGroup>
<BuildTargetsVersion>2.0</BuildTargetsVersion>
<!--Set this to true if you edit this file to prevent automatic updates-->
<BuildTargetsModified>false</BuildTargetsModified>
<!--Output assembly path without extension-->
<OutputAssemblyName>$(OutputPath)$(AssemblyName)</OutputAssemblyName>
<!--Path to folder to be zipped. Needs to be relative to the project directory to work without changes to the 'BuildForCI' target.-->
<ArtifactDestination>$(OutputPath)Final</ArtifactDestination>
<ErrorOnMismatchedVersions Condition="'$(Configuration)' == 'Release'">True</ErrorOnMismatchedVersions>
</PropertyGroup>
<!--Build Targets-->
<!--Displays a warning if BeatSaberModdingTools.Tasks is not installed.-->
<Target Name="CheckBSMTInstalled" AfterTargets="BeforeBuild" Condition="'$(BSMTTaskAssembly)' == ''">
<Warning Text="The BeatSaberModdingTools.Tasks nuget package doesn't seem to be installed, advanced build targets will not work." />
</Target>
<!--Runs a build task to get info about the project used by later targets.-->
<Target Name="GetProjectInfo" AfterTargets="CheckBSMTInstalled" DependsOnTargets="CheckBSMTInstalled" Condition="'$(BSMTTaskAssembly)' != ''">
<Message Text="Using AssemblyVersion defined in project instead of 'Properties\AssemblyInfo.cs'" Importance="high" Condition="'$(AssemblyVersion)' != ''" />
<GetManifestInfo FailOnError="$(ErrorOnMismatchedVersions)">
<Output TaskParameter="PluginVersion" PropertyName="PluginVersion" />
<Output TaskParameter="BasePluginVersion" PropertyName="BasePluginVersion" />
<Output TaskParameter="GameVersion" PropertyName="GameVersion" />
</GetManifestInfo>
<PropertyGroup>
<AssemblyVersion>$(BasePluginVersion)</AssemblyVersion>
<FileVersion>$(BasePluginVersion)</FileVersion>
<InformationalVersion>$(BasePluginVersion)</InformationalVersion>
</PropertyGroup>
<GetCommitInfo ProjectDir="$(ProjectDir)">
<Output TaskParameter="CommitHash" PropertyName="CommitHash" />
<Output TaskParameter="Branch" PropertyName="Branch" />
<Output TaskParameter="Modified" PropertyName="GitModified" />
</GetCommitInfo>
<PropertyGroup>
<!--Build name for artifact/zip file-->
<ArtifactName>$(AssemblyName)</ArtifactName>
<ArtifactName Condition="'$(PluginVersion)' != ''">$(ArtifactName)-$(PluginVersion)</ArtifactName>
<ArtifactName Condition="'$(GameVersion)' != ''">$(ArtifactName)-bs$(GameVersion)</ArtifactName>
<ArtifactName Condition="'$(CommitHash)' != '' AND '$(CommitHash)' != 'local'">$(ArtifactName)-$(CommitHash)</ArtifactName>
</PropertyGroup>
</Target>
<!--Build target for Continuous Integration builds. Set up for GitHub Actions.-->
<Target Name="BuildForCI" AfterTargets="Build" DependsOnTargets="GetProjectInfo" Condition="'$(ContinuousIntegrationBuild)' == 'True' AND '$(BSMTTaskAssembly)' != ''">
<PropertyGroup>
<!--Set 'ArtifactName' if it failed before.-->
<ArtifactName Condition="'$(ArtifactName)' == ''">$(AssemblyName)</ArtifactName>
</PropertyGroup>
<Message Text="Building for CI" Importance="high" />
<Message Text="PluginVersion: $(PluginVersion), AssemblyVersion: $(AssemblyVersion), GameVersion: $(GameVersion)" Importance="high" />
<Message Text="::set-output name=filename::$(ArtifactName)" Importance="high" />
<Message Text="::set-output name=assemblyname::$(AssemblyName)" Importance="high" />
<Message Text="::set-output name=artifactpath::$(ProjectDir)$(ArtifactDestination)" Importance="high" />
<Message Text="Copying '$(OutputAssemblyName).dll' to '$(ProjectDir)$(ArtifactDestination)\Plugins\$(AssemblyName).dll'" Importance="high" />
<Copy SourceFiles="$(OutputAssemblyName).dll" DestinationFiles="$(ProjectDir)$(ArtifactDestination)\Plugins\$(AssemblyName).dll" />
</Target>
<!--Creates a BeatMods compliant zip file with the release.-->
<Target Name="ZipRelease" AfterTargets="Build" Condition="'$(DisableZipRelease)' != 'True' AND '$(Configuration)' == 'Release' AND '$(BSMTTaskAssembly)' != ''">
<PropertyGroup>
<!--Set 'ArtifactName' if it failed before.-->
<ArtifactName Condition="'$(ArtifactName)' == ''">$(AssemblyName)</ArtifactName>
<DestinationDirectory>$(OutDir)zip\</DestinationDirectory>
</PropertyGroup>
<ItemGroup>
<OldZips Include="$(DestinationDirectory)$(AssemblyName)*.zip"/>
</ItemGroup>
<Copy SourceFiles="$(OutputAssemblyName).dll" DestinationFiles="$(ArtifactDestination)\Plugins\$(AssemblyName).dll" />
<Message Text="PluginVersion: $(PluginVersion), AssemblyVersion: $(AssemblyVersion), GameVersion: $(GameVersion)" Importance="high" />
<Delete Files="@(OldZips)" TreatErrorsAsWarnings="true" ContinueOnError="true" />
<ZipDir SourceDirectory="$(ArtifactDestination)" DestinationFile="$(DestinationDirectory)$(ArtifactName).zip" />
</Target>
<!--Copies the assembly and pdb to the Beat Saber folder.-->
<Target Name="CopyToPlugins" AfterTargets="Build" Condition="'$(DisableCopyToPlugins)' != 'True' AND '$(ContinuousIntegrationBuild)' != 'True'">
<PropertyGroup>
<PluginDir>$(BeatSaberDir)\Plugins</PluginDir>
<CanCopyToPlugins>True</CanCopyToPlugins>
<CopyToPluginsError Condition="!Exists('$(PluginDir)')">Unable to copy assembly to game folder, did you set 'BeatSaberDir' correctly in your 'csproj.user' file? Plugins folder doesn't exist: '$(PluginDir)'.</CopyToPluginsError>
<!--Error if 'BeatSaberDir' does not have 'Beat Saber.exe'-->
<CopyToPluginsError Condition="!Exists('$(BeatSaberDir)\Beat Saber.exe')">Unable to copy to Plugins folder, '$(BeatSaberDir)' does not appear to be a Beat Saber game install.</CopyToPluginsError>
<!--Error if 'BeatSaberDir' is the same as 'LocalRefsDir'-->
<CopyToPluginsError Condition="'$(BeatSaberDir)' == '$(LocalRefsDir)' OR '$(BeatSaberDir)' == ''">Unable to copy to Plugins folder, 'BeatSaberDir' has not been set in your 'csproj.user' file.</CopyToPluginsError>
<CanCopyToPlugins Condition="'$(CopyToPluginsError)' != ''">False</CanCopyToPlugins>
</PropertyGroup>
<!--Check if Beat Saber is running-->
<IsProcessRunning ProcessName="Beat Saber" Condition="'$(BSMTTaskAssembly)' != ''">
<Output TaskParameter="IsRunning" PropertyName="IsRunning" />
</IsProcessRunning>
<PropertyGroup>
<!--If Beat Saber is running, output to the Pending folder-->
<PluginDir Condition="'$(IsRunning)' == 'True'">$(BeatSaberDir)\IPA\Pending\Plugins</PluginDir>
</PropertyGroup>
<Warning Text="$(CopyToPluginsError)" Condition="'$(CopyToPluginsError)' != ''" />
<Message Text="Copying '$(OutputAssemblyName).dll' to '$(PluginDir)'." Importance="high" Condition="$(CanCopyToPlugins)" />
<Copy SourceFiles="$(OutputAssemblyName).dll" DestinationFiles="$(PluginDir)\$(AssemblyName).dll" Condition="$(CanCopyToPlugins)" />
<Copy SourceFiles="$(OutputAssemblyName).pdb" DestinationFiles="$(PluginDir)\$(AssemblyName).pdb" Condition="'$(CanCopyToPlugins)' == 'True' AND Exists('$(OutputAssemblyName).pdb')" />
<Warning Text="Beat Saber is running, restart the game to use the latest build." Condition="'$(IsRunning)' == 'True'" />
</Target>
</Project>
142 changes: 142 additions & 0 deletions HarmonyPatches/GeneralSwingData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace OverswingCounter.HarmonyPatches {
[HarmonyPatch(typeof(GameNoteController), nameof(GameNoteController.HandleCut))]
static class GeneralSwingData {
public static Dictionary<SaberType, CutInfo> currentPrimaryCut;
public static Dictionary<SaberType, CutInfo> lastFinishedCut;

[HarmonyPriority(int.MaxValue)]
static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {
var res = instructions.ToList();

res.InsertRange(41, new[] {
new CodeInstruction(OpCodes.Ldarg_1), // 1. Argument, Saber

new CodeInstruction(OpCodes.Ldloc_S, 6), // The local variable of the new SaberSwingRatingCounter instance

//new CodeInstruction(OpCodes.Ldarg_0),
//new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(GameNoteController), "_noteTransform")),

new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(GeneralSwingData), nameof(PrepareNewSwingRatingCounter)))
});

return res;
}

public static Dictionary<SaberSwingRatingCounter, CutInfo> swingRatingInfos { get; private set; } = new Dictionary<SaberSwingRatingCounter, CutInfo>();

static void PrepareNewSwingRatingCounter(Saber saber, SaberSwingRatingCounter counter/*, Transform transform*/) {
#if TRACE
Console.WriteLine("GameNoteController.HandleCut => PrepareNewSwingRatingCounter()");
#endif

if(!swingRatingInfos.TryGetValue(counter, out var existing) || existing.counter != counter)
swingRatingInfos[counter] = new CutInfo(counter, saber/*, transform.localPosition*/);
}

public static void Clear() {
currentPrimaryCut = new Dictionary<SaberType, CutInfo>(2) {
{ SaberType.SaberA, null },
{ SaberType.SaberB, null },
};

lastFinishedCut = new Dictionary<SaberType, CutInfo>(2) {
{ SaberType.SaberA, null },
{ SaberType.SaberB, null },
};

swingRatingInfos.Clear();
}

public static Action<CutInfo> newCutCompleted;
}

public class CutInfo : ISaberSwingRatingCounterDidFinishReceiver {
public SaberSwingRatingCounter counter;
public SaberType saberType;
public Saber saber;

public float beforeRating = 0f;
public float correctedBeforeRating = 0f;
public float afterRating = 0f;
public float correctedAfterRating = 0f;
public float angle = 0f;

//public Vector2 notePosition2D;

public Vector2 swingStart;
public Vector2 swingEnd;

public float cutTime;

public bool isDownswing => angle <= 10f || angle >= 170f;


public bool isPrimary { get; private set; } = false;
public CutInfo lastFinishedCutToCompareAgainst { get; private set; }

public CutInfo(SaberSwingRatingCounter counter, Saber saber/*, Vector3 notePosition*/) {
this.counter = counter;
this.saber = saber;
saberType = saber.saberType;
cutTime = Time.realtimeSinceStartup;
//notePosition2D = new Vector2(notePosition.x, notePosition.y);

lastFinishedCutToCompareAgainst = GeneralSwingData.lastFinishedCut[saberType];

isPrimary = GeneralSwingData.currentPrimaryCut[saberType] == null;
if(isPrimary)
GeneralSwingData.currentPrimaryCut[saberType] = this;

counter.RegisterDidFinishReceiver(this);
}

public void HandleSaberSwingRatingCounterDidFinish(ISaberSwingRatingCounter saberSwingRatingCounter) {
counter.UnregisterDidFinishReceiver(this);
swingEnd = saber.saberBladeTopPos;
#if DEBUG
Console.WriteLine("Counter {0} (Primary: {5}) finished ({4})! {1:P2}pre, {2:P2}post, {3}angle (isDown: {6})", counter.GetHashCode(), beforeRating, afterRating, angle, saberType, isPrimary, isDownswing);
#endif
counter = null;

/*
* When you are overswinging, you are always overswinging the exact angle that you do overswing,
* but underswinging is a special case where you can *theoretically* be underswinging, but in reality,
* due to swing points being rounded per cut, you might still be getting full points. We kinda need to
* account for that and calculate your theoretical swing angle based off the points you got
*/
correctedAfterRating = afterRating;
if(afterRating < 1f) {
var cCorrectedAfterRating = (float)Math.Round(afterRating * ScoreModel.kMaxAfterCutSwingRawScore) / ScoreModel.kMaxAfterCutSwingRawScore;

// We only want to round UP, not down
if(cCorrectedAfterRating > afterRating)
correctedAfterRating = cCorrectedAfterRating;
}

correctedBeforeRating = beforeRating;
if(beforeRating < 1f) {
var cCorrectedBeforeRating = (float)Math.Round(beforeRating * ScoreModel.kMaxBeforeCutSwingRawScore) / ScoreModel.kMaxBeforeCutSwingRawScore;

if(cCorrectedBeforeRating > beforeRating)
correctedBeforeRating = cCorrectedBeforeRating;
}

GeneralSwingData.lastFinishedCut[saberType] = this;

if(GeneralSwingData.currentPrimaryCut[saberType] == this)
GeneralSwingData.currentPrimaryCut[saberType] = null;

if(GeneralSwingData.newCutCompleted != null)
GeneralSwingData.newCutCompleted(this);
}
}
}
61 changes: 61 additions & 0 deletions HarmonyPatches/PostswingData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace OverswingCounter.HarmonyPatches {
[HarmonyPatch(typeof(SaberSwingRatingCounter), nameof(SaberSwingRatingCounter.ProcessNewData))]
static class GetUnclampedPostSwingData {
static MethodBase SaberSwingRating_AfterCutStepRating = AccessTools.Method(typeof(SaberSwingRating), nameof(SaberSwingRating.AfterCutStepRating));
static FieldInfo SaberSwingRatingCounter_afterCutRating = AccessTools.Field(typeof(SaberSwingRatingCounter), "_afterCutRating");

static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {
var res = instructions.ToList();

bool waitForStfld = false;

var interceptor = new [] {
new CodeInstruction(OpCodes.Ldarg_0), // this

new CodeInstruction(OpCodes.Ldarg_0), // this._afterCutRating
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(SaberSwingRatingCounter), "_afterCutRating")),

new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(GetUnclampedPostSwingData), nameof(AccumulatorFn)))
};

for(var idx = 0; idx < res.Count; idx++) {
var elem = res[idx];

if(waitForStfld) {
if(elem.opcode != OpCodes.Stfld || elem.operand != (object)SaberSwingRatingCounter_afterCutRating)
continue;

res.InsertRange(idx + 1, interceptor);
idx += interceptor.Length;

waitForStfld = false;
} else if(elem.opcode == OpCodes.Call && elem.operand == (object)SaberSwingRating_AfterCutStepRating) {
waitForStfld = true;
}
}

return res;
}

public static void AccumulatorFn(SaberSwingRatingCounter __instance, float tmpRating) {
if(!GeneralSwingData.swingRatingInfos.TryGetValue(__instance, out var info) || info.counter == null)
return;

if(tmpRating <= 1f) {
info.afterRating = tmpRating;
} else {
info.afterRating += tmpRating - 1f;
}
}
}
}
Loading

0 comments on commit 1535521

Please sign in to comment.