Compare commits
3 Commits
1.0.0-pre.
...
main
Author | SHA1 | Date | |
---|---|---|---|
afe095286b | |||
e7c0dad0d7 | |||
9c180657b4 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
*.dll
|
||||
*.csproj
|
||||
bin/
|
||||
obj/
|
||||
obj/
|
||||
dist/
|
47
Inohara.DT/BatchManual.cs
Normal file
47
Inohara.DT/BatchManual.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System;
|
||||
|
||||
namespace Inohara.DT;
|
||||
|
||||
[Serializable]
|
||||
public struct BatchManual {
|
||||
public BatchManualMeta meta;
|
||||
public BatchManualScore[] scores;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchManualMeta {
|
||||
public string game;
|
||||
public string playtype;
|
||||
public string service;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchManualScore {
|
||||
public int score;
|
||||
public string difficulty;
|
||||
public UInt64 timeAchieved;
|
||||
public string noteLamp;
|
||||
public string bellLamp;
|
||||
public string matchType;
|
||||
public string identifier;
|
||||
public BatchManualJudgements judgements;
|
||||
public BatchManualOptional optional;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchManualJudgements {
|
||||
public int cbreak;
|
||||
public int breakMyBonesIWill;
|
||||
public int hit;
|
||||
public int miss;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchManualOptional {
|
||||
public int fast;
|
||||
public int slow;
|
||||
public int bellCount;
|
||||
public int totalBellCount;
|
||||
public int damage;
|
||||
public int platScore;
|
||||
}
|
34
Inohara.DT/BatchManualPB.cs
Normal file
34
Inohara.DT/BatchManualPB.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
namespace Inohara.DT;
|
||||
|
||||
[Serializable]
|
||||
public struct BatchManualPB {
|
||||
public BatchManualPBMeta meta;
|
||||
public BatchManualPBScore[] scores;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchManualPBMeta {
|
||||
public string game;
|
||||
public string playtype;
|
||||
public string service;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchManualPBScore {
|
||||
public int score;
|
||||
public string difficulty;
|
||||
public string noteLamp;
|
||||
public string bellLamp;
|
||||
public string matchType;
|
||||
public string identifier;
|
||||
public BatchManualPBJudgements judgements;
|
||||
public BatchManualPBOptional optional;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchManualPBJudgements {}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchManualPBOptional {}
|
19
Inohara.DT/BatchResponse.cs
Normal file
19
Inohara.DT/BatchResponse.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Inohara.DT;
|
||||
|
||||
[Serializable]
|
||||
public struct BatchResponse {
|
||||
public bool success;
|
||||
public BatchResponseBody body;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchResponseBody {
|
||||
public string url;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchResponse2 {
|
||||
public bool success;
|
||||
}
|
16
Inohara.DT/StatusResponse.cs
Normal file
16
Inohara.DT/StatusResponse.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Inohara.DT;
|
||||
|
||||
[Serializable]
|
||||
public struct StatusResponse {
|
||||
public bool success;
|
||||
public StatusBody body;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct StatusBody {
|
||||
public string version;
|
||||
public string whoami;
|
||||
public string[] permissions;
|
||||
}
|
@ -16,4 +16,10 @@
|
||||
<Reference Include="UnityEngine.UI"><HintPath></HintPath></Reference>
|
||||
<Reference Include="Mu3Assembly"><HintPath></HintPath></Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="Dist" AfterTargets="Build">
|
||||
<Copy SourceFiles="$(TargetDir)$(AssemblyName).dll" DestinationFolder="dist" />
|
||||
<Copy SourceFiles="$(TargetDir)$(AssemblyName).dll" DestinationFolder="dist/BepInEx/monomod" />
|
||||
<ZipDirectory SourceDirectory="dist/BepInEx" DestinationFile="dist/inohara.zip" Overwrite="true" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
@ -1,92 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Inohara;
|
||||
|
||||
/**
|
||||
* Batch manual
|
||||
*/
|
||||
|
||||
[Serializable]
|
||||
public struct BatchManual {
|
||||
public BatchMeta meta;
|
||||
public BatchScore[] scores;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchMeta {
|
||||
public string game;
|
||||
public string playtype;
|
||||
public string service;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchScore {
|
||||
public int score;
|
||||
public string difficulty;
|
||||
public UInt64 timeAchieved;
|
||||
public string noteLamp;
|
||||
public string bellLamp;
|
||||
public string matchType;
|
||||
public string identifier;
|
||||
public BatchJudgements judgements;
|
||||
public BatchOptional optional;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchJudgements {
|
||||
public int cbreak;
|
||||
public int breakMyBonesIWill;
|
||||
public int hit;
|
||||
public int miss;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchOptional {
|
||||
public int fast;
|
||||
public int slow;
|
||||
public int bellCount;
|
||||
public int totalBellCount;
|
||||
public int damage;
|
||||
public int platScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* BM response 1
|
||||
*/
|
||||
|
||||
[Serializable]
|
||||
public struct BatchResponse {
|
||||
public bool success;
|
||||
public BatchResponseBody body;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct BatchResponseBody {
|
||||
public string url;
|
||||
}
|
||||
|
||||
/**
|
||||
* BM response 2
|
||||
*/
|
||||
|
||||
[Serializable]
|
||||
public struct BatchResponse2 {
|
||||
public bool success;
|
||||
}
|
||||
|
||||
/**
|
||||
* API status response
|
||||
*/
|
||||
|
||||
[Serializable]
|
||||
public struct StatusResponse {
|
||||
public bool success;
|
||||
public StatusBody body;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct StatusBody {
|
||||
public string version;
|
||||
public string whoami;
|
||||
public string[] permissions;
|
||||
}
|
@ -11,6 +11,7 @@ using MU3.Util;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.UI;
|
||||
using Inohara.DT;
|
||||
|
||||
namespace Inohara;
|
||||
|
||||
@ -22,6 +23,7 @@ public class Exporter: SingletonMonoBehaviour<Exporter> {
|
||||
public string StatusPoint;
|
||||
public string ImportPoint;
|
||||
public bool EnableText;
|
||||
public bool ExportPBs;
|
||||
}
|
||||
static readonly float REQ2_DELAY = 4f;
|
||||
static readonly float DRAW_DURATION = 4f;
|
||||
@ -30,11 +32,11 @@ public class Exporter: SingletonMonoBehaviour<Exporter> {
|
||||
private readonly Dictionary<string, string> _tokens = new();
|
||||
private string _currToken = "";
|
||||
private readonly Font _arial = Resources.GetBuiltinResource<Font>("Arial.ttf");
|
||||
private List<BatchScore> _scores = new();
|
||||
private List<BatchManualScore> _scores = new();
|
||||
|
||||
private void Log(object o, bool text) {
|
||||
private void Log(object o, bool drawText) {
|
||||
Debug.Log("[Inohara] " + o.ToString());
|
||||
if(_cfg.EnableText && text) {
|
||||
if(_cfg.EnableText && drawText) {
|
||||
StartCoroutine(DrawMessage(o.ToString(), new Color(1f, 1f, 1f, 1.0f)));
|
||||
}
|
||||
}
|
||||
@ -54,6 +56,7 @@ public class Exporter: SingletonMonoBehaviour<Exporter> {
|
||||
}
|
||||
|
||||
public void LoadCfg() {
|
||||
_cfg.Enable = false;
|
||||
try {
|
||||
using StreamReader reader = new(Path.Combine(Application.dataPath, "../inohara.cfg"));
|
||||
Dictionary<string, string> options = new();
|
||||
@ -81,6 +84,9 @@ public class Exporter: SingletonMonoBehaviour<Exporter> {
|
||||
if(!options.ContainsKey("enableosd") || !bool.TryParse(options["enableosd"], out _cfg.EnableText)) {
|
||||
_cfg.EnableText = false;
|
||||
}
|
||||
if(!options.ContainsKey("exportpbs") || !bool.TryParse(options["exportpbs"], out _cfg.ExportPBs)) {
|
||||
_cfg.ExportPBs = false;
|
||||
}
|
||||
if(!options.ContainsKey("timeout") || !int.TryParse(options["timeout"], out _cfg.Timeout)) {
|
||||
_cfg.Timeout = 3;
|
||||
}
|
||||
@ -136,6 +142,7 @@ public class Exporter: SingletonMonoBehaviour<Exporter> {
|
||||
using var req = new UnityWebRequest(_cfg.BaseUrl + _cfg.StatusPoint, "GET");
|
||||
req.SetRequestHeader("Authorization", "Bearer " + tmpBearer);
|
||||
req.downloadHandler = new DownloadHandlerBuffer();
|
||||
req.timeout = _cfg.Timeout;
|
||||
|
||||
yield return req.Send();
|
||||
|
||||
@ -221,6 +228,27 @@ public class Exporter: SingletonMonoBehaviour<Exporter> {
|
||||
}
|
||||
}
|
||||
|
||||
public void ExportPBs() {
|
||||
if(!_cfg.ExportPBs) {
|
||||
return;
|
||||
}
|
||||
|
||||
var userMusic = Singleton<UserManager>.instance.userMusic;
|
||||
var scores = new List<BatchManualPBScore>();
|
||||
foreach(var x in userMusic.Values) {
|
||||
foreach(var y in x.UserFumen) {
|
||||
if(y != null) {
|
||||
scores.Add(Util.CreatePB(y));
|
||||
}
|
||||
}
|
||||
}
|
||||
var batch = Util.CreatePBBatch(scores);
|
||||
using StreamWriter writer = new(Path.Combine(Application.dataPath, "../batch-manual.json"));
|
||||
writer.Write(batch);
|
||||
|
||||
Log("Exported PBs to batch-manual.json", false);
|
||||
}
|
||||
|
||||
// This is just for fun
|
||||
private IEnumerator DrawMessage(string message, Color color) {
|
||||
GameObject canvasGO = new() {
|
||||
|
@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Inohara.DT;
|
||||
using MU3.Battle;
|
||||
using MU3.DataStudio;
|
||||
using MU3.Game;
|
||||
using MU3.User;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Inohara;
|
||||
@ -35,9 +37,9 @@ class Util {
|
||||
};
|
||||
}
|
||||
|
||||
public static BatchScore CreateScore(BattleResult result, SessionInfo info) {
|
||||
public static BatchManualScore CreateScore(BattleResult result, SessionInfo info) {
|
||||
var timestampSec = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
|
||||
return new BatchScore {
|
||||
return new BatchManualScore {
|
||||
score = result.technicalScore,
|
||||
difficulty = GetStringDiff(info.musicLevel),
|
||||
timeAchieved = (ulong)timestampSec * 1000,
|
||||
@ -45,7 +47,7 @@ class Util {
|
||||
identifier = info.musicData.id.ToString(),
|
||||
bellLamp = result.bellComboResult == BellComboResult.None ? "NONE" : "FULL BELL",
|
||||
noteLamp = GetLamp(result),
|
||||
optional = new BatchOptional() {
|
||||
optional = new BatchManualOptional() {
|
||||
fast = result.numNotesFast,
|
||||
slow = result.numNotesLate,
|
||||
bellCount = result.numBellCatch,
|
||||
@ -53,7 +55,7 @@ class Util {
|
||||
damage = result.countDamage,
|
||||
platScore = result.platinumScore
|
||||
},
|
||||
judgements = new BatchJudgements() {
|
||||
judgements = new BatchManualJudgements() {
|
||||
cbreak = result.numNotesCBreak,
|
||||
breakMyBonesIWill = result.numNotesBreak,
|
||||
hit = result.numNotesHit,
|
||||
@ -62,9 +64,28 @@ class Util {
|
||||
};
|
||||
}
|
||||
|
||||
public static string CreateBatch(List<BatchScore> scores) {
|
||||
public static BatchManualPBScore CreatePB(UserFumen fumen) {
|
||||
return new BatchManualPBScore {
|
||||
score = fumen.TechScoreMax,
|
||||
difficulty = GetStringDiff(fumen.Level),
|
||||
matchType = "inGameID",
|
||||
identifier = fumen.MusicId.ToString(),
|
||||
bellLamp = fumen.IsFullBell ? "FULL BELL" : "NONE",
|
||||
noteLamp =
|
||||
fumen.IsAllBreak ? "ALL BREAK" :
|
||||
fumen.IsFullCombo ? "FULL COMBO" :
|
||||
// fumen.isClear is a bool that seems to flag battle victory. Useless
|
||||
// basing this on score is ass but for the most part it will be accurate.
|
||||
fumen.TechScoreMax >= 940000 ? "CLEAR" :
|
||||
"LOSS",
|
||||
optional = new BatchManualPBOptional() {},
|
||||
judgements = new BatchManualPBJudgements() {}
|
||||
};
|
||||
}
|
||||
|
||||
public static string CreateBatch(List<BatchManualScore> scores) {
|
||||
var bm = new BatchManual {
|
||||
meta = new BatchMeta {
|
||||
meta = new BatchManualMeta {
|
||||
game = "ongeki",
|
||||
playtype = "Single",
|
||||
service = "inohara"
|
||||
@ -74,4 +95,17 @@ class Util {
|
||||
|
||||
return JsonUtility.ToJson(bm).Replace("breakMyBonesIWill", "break");
|
||||
}
|
||||
|
||||
public static string CreatePBBatch(List<BatchManualPBScore> scores) {
|
||||
var bm = new BatchManualPB {
|
||||
meta = new BatchManualPBMeta {
|
||||
game = "ongeki",
|
||||
playtype = "Single",
|
||||
service = "inohara-pb"
|
||||
},
|
||||
scores = scores.ToArray()
|
||||
};
|
||||
|
||||
return JsonUtility.ToJson(bm);
|
||||
}
|
||||
}
|
@ -9,16 +9,16 @@ using MU3.Util;
|
||||
namespace MU3.App;
|
||||
|
||||
public class patch_ApplicationMU3 : ApplicationMU3 {
|
||||
private extern void orig_Execute_WaitAMDaemonReady();
|
||||
private extern void orig_initializeAfterAMDaemonReady();
|
||||
|
||||
private void Execute_WaitAMDaemonReady() {
|
||||
private void initializeAfterAMDaemonReady() {
|
||||
GameObject go = new() {
|
||||
name = "Inohara"
|
||||
};
|
||||
go.AddComponent<Inohara.Exporter>();
|
||||
|
||||
DontDestroyOnLoad(go);
|
||||
orig_Execute_WaitAMDaemonReady();
|
||||
orig_initializeAfterAMDaemonReady();
|
||||
SingletonMonoBehaviour<Inohara.Exporter>.instance.LoadCfg();
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,15 @@ namespace MU3;
|
||||
public class patch_Scene_25_Login : Scene_25_Login {
|
||||
|
||||
private extern void orig_finishAime();
|
||||
private extern void orig_GetUserRival_Init();
|
||||
|
||||
private void finishAime() {
|
||||
orig_finishAime();
|
||||
SingletonMonoBehaviour<Inohara.Exporter>.instance.Authorize();
|
||||
}
|
||||
|
||||
private void GetUserRival_Init() {
|
||||
orig_GetUserRival_Init();
|
||||
SingletonMonoBehaviour<Inohara.Exporter>.instance.ExportPBs();
|
||||
}
|
||||
}
|
||||
|
52
README.md
52
README.md
@ -8,31 +8,55 @@ A µ3 score exporter for [Tachi](https://github.com/zkldi/Tachi).
|
||||
- 1.45
|
||||
|
||||
### Installation
|
||||
Get the config file [here](https://kamai.tachi.ac/client-file-flow/CIa914320cd344a8db712cf0c99254c205ca940463), download the DLL [here](https://gitea.tendokyu.moe/akanyan/inohara/releases) and follow one of the methods below.
|
||||
First, get the config file [here](https://kamai.tachi.ac/client-file-flow/CIa914320cd344a8db712cf0c99254c205ca940463) and put it in the base game directory (next to `mu3.exe`), then follow one of the methods below.
|
||||
|
||||
#### The BepInEx method (recommended)
|
||||
- Download [BepInEx](https://github.com/BepInEx/BepInEx/releases/)
|
||||
- Copy the `BepInEx` directory into the base game directory (where `mu3.exe` is); omit `winhttp.dll`
|
||||
#### The simple method
|
||||
- Update [segatools](https://gitea.tendokyu.moe/Dniel97/segatools).
|
||||
- Download `inohara.zip` from [releases](https://gitea.tendokyu.moe/akanyan/inohara/releases) and extract it into the base game directory.
|
||||
- Modify this entry in `segatools.ini`:
|
||||
```ini
|
||||
[unity]
|
||||
targetAssembly=BepInEx\core\BepInEx.Preloader.dll
|
||||
```
|
||||
- The game directory should look like this (abridged):
|
||||
```
|
||||
├── BepInEx
|
||||
├── mu3_Data
|
||||
├── inohara.cfg
|
||||
├── mu3.exe
|
||||
└── segatools.ini
|
||||
```
|
||||
|
||||
#### The manual BepInEx method
|
||||
- Download [BepInEx 5](https://github.com/BepInEx/BepInEx/releases/) and [BepInEx.MonoMod.Loader](https://github.com/BepInEx/BepInEx.MonoMod.Loader).
|
||||
- Copy both `BepInEx` directories into the base game directory; omit `winhttp.dll`.
|
||||
- Modify this entry in `segatools.ini`:
|
||||
```ini
|
||||
[unity]
|
||||
targetAssembly=BepInEx\core\BepInEx.Preloader.dll
|
||||
```
|
||||
- If you don't have this entry, update segatools.
|
||||
- If you insist on not updating segatools, instead copy `winhttp.dll` and rename it to `version.dll`.
|
||||
- Move `Assembly-CSharp.Inohara.mm.dll` to `BepInEx\monomod`
|
||||
- Put `inohara.cfg` in the base game directory (next to `mu3.exe`)
|
||||
- Download `Assembly-CSharp.Inohara.mm.dll` from [releases](https://gitea.tendokyu.moe/akanyan/inohara/releases) and put it in `BepInEx\monomod`.
|
||||
|
||||
#### The MonoMod method
|
||||
- Download [MonoMod](https://github.com/MonoMod/MonoMod/releases)
|
||||
- Copy `Assembly-CSharp.Inohara.mm.dll` into `mu3_Data\Managed`
|
||||
- Run `MonoMod.exe mu3_Data\Managed\Assembly-CSharp.dll`
|
||||
- Backup `Assembly-CSharp.dll`
|
||||
- Rename `MONOMODDED_Assembly-CSharp.dll` to `Assembly-CSharp.dll`
|
||||
- Move `inohara.cfg` to the base game directory (next to `mu3.exe`)
|
||||
|
||||
#### The hardpatch method
|
||||
- Download [MonoMod](https://github.com/MonoMod/MonoMod/releases).
|
||||
- Download `Assembly-CSharp.Inohara.mm.dll` from [releases](https://gitea.tendokyu.moe/akanyan/inohara/releases) and put it in `mu3_Data\Managed`.
|
||||
- Run:
|
||||
```
|
||||
MonoMod.exe mu3_Data\Managed\Assembly-CSharp.dll
|
||||
```
|
||||
- Backup `Assembly-CSharp.dll`.
|
||||
- Rename `MONOMODDED_Assembly-CSharp.dll` to `Assembly-CSharp.dll`.
|
||||
|
||||
### Usage
|
||||
Scores are sent after each play and that's it. You can nonetheless make sure it's running by checking the console or toggling `EnableOSD`.
|
||||
Scores are sent after each play and that's it. You can nonetheless make sure it's running by enabling the console in `BepInEx\config\BepInEx.cfg` or the OSD in `inohara.cfg`.
|
||||
|
||||
#### Uploading older scores
|
||||
Use [this script](https://gist.github.com/nyairobi/ffdf9e674f31987b1ffbd38d31b55f6c). You only have to do this once as Inohara will handle all future scores. If you are on a remote server, contact the admin.
|
||||
|
||||
**As a last resort**, you can toggle `ExportPBs` and upload the generated `batch-manual.json` [here](https://kamai.tachi.ac/import/batch-manual). Those PBs are industrial grade dogshit. Should the server admin provide you access to the playlog down the line, delete the `inohara-pb` import.
|
||||
|
||||
### Building
|
||||
Provide your own `Assembly-CSharp.dll` (or `_unpacked`) and `UnityEngine.UI.dll`, then `dotnet restore`, `dotnet build`.
|
@ -1,18 +1,29 @@
|
||||
[Options]
|
||||
|
||||
# Whether to enable score submissions
|
||||
Enable = true
|
||||
|
||||
# Timeout for web requests, in seconds
|
||||
Timeout = 3
|
||||
|
||||
# Tachi instance base URL
|
||||
BaseUrl =
|
||||
BaseUrl = https://kamai.tachi.ac
|
||||
|
||||
# Tachi status endpoint
|
||||
Status = /api/v1/status
|
||||
|
||||
# Tachi score import endpoint
|
||||
Import = /ir/direct-manual/import
|
||||
# Display status on-screen (rudimentarily)
|
||||
|
||||
# Whether to display status on-screen (rudimentarily)
|
||||
EnableOSD = false
|
||||
|
||||
# Whether to export your PBs for batch-manual (saved to batch-manual.json)
|
||||
# This is very, very bad and should only be used as last resort
|
||||
ExportPBs = false
|
||||
|
||||
[Keys]
|
||||
|
||||
* = %%TACHI_KEY%%
|
||||
|
||||
# If you have a multi-user setup, you can configure
|
||||
|
Loading…
Reference in New Issue
Block a user