15レス(1/1)
1:
プログラム初心者 ◆l3Flv.uj/A2018/08/15(水) 17:38:17
欲しいのなければ自分で作れと思い最近作り始めてxmlまでは順調にできたんだが
C#になってから全くできないんだ(プログラム初心者)
Harmony(HugsLib)を使えばprivateのフィールドやメソッドにアクセスして
既存クラスを変えられるらしいが変え方が全く分からない
Harmony使っている人のソース見ても英wiki見ても理解ができない

今作ろうとしているのは襲撃ポイント変更mod
StoryWatcher_RampUpクラスに「時間経過による襲撃ポイント増加」に関わりそうな
RampUpWatcherTickメソッドを弄れば固定にしたりなくしたり
できそうなんだけどprivateだから外部から弄れないし

かれこれ、丸3日何も出来てないのでHarmony詳しい人教えてください
2:
プログラム初心者 ◆l3Flv.uj/A[sage] 2018/08/15(水) 17:56:32
StoryWatcher_RampUpクラスで元のソースが

private float shortTermFactor = 1f;
private float longTermFactor = 1f;

public void RampUpWatcherTick()
{
if (Find.TickManager.TicksGame % 5000 == 0)
{
if ((float)GenDate.DaysPassed >= 21f)
{
this.shortTermFactor += 0.000356125354f;
}
if ((float)GenDate.DaysPassed >= 42f)
{
this.longTermFactor += 0.000231481492f;
}
}
}
続き13行
3:
プログラム初心者 ◆l3Flv.uj/A[sage] 2018/08/16(木) 14:31:00
注意:下記コードは試行錯誤中で動きません。
namespace SR_IR_fixedvalue
{
[StaticConstructorOnStartup]
class Main
{
static Main()
{
var harmony = HarmonyInstance.Create("com.test.fixedvalue");
harmony.PatchAll(Assembly.GetExecutingAssembly());
}
}

[HarmonyPatch(typeof(StoryWatcher_RampUp))]
[HarmonyPatch("RampUpWatcherTick")]

class Patch
{
static void PostFix(StoryWatcher_RampUp __instance)
{続き31行
4(1):
名無しさん[sage] 2018/08/16(木) 16:21:16
Harmony、そんなに詳しくはないですが。。。
ちなみに、Patchクラス での"this"はPatchクラスのインスタンスになりますよ。まぁ、static method なので参照できないと思いますけど。
StoryWatcher_RampUp クラスのインスタンスは __instance 引数に設定されますね。 https://github.com/pardeike/Harmony/wiki/Patching#patch-parameters
__instance の private field にアクセスしたいなら、普通にReflectionしてそのままSetValueする(A)か、DynamicMethodでGetter/SetterをILGenerateしたDelegateを呼び出す(B)かですね。

[HarmonyPatch(typeof(StoryWatcher_RampUp))]
[HarmonyPatch("RampUpWatcherTick")]
class Patch
{
// (A) Reflection して Field を取得
private static System.Reflection.FieldInfo shortTermFactorField = typeof(StoryWatcher_RampUp).GetField("shortTermFactor", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

// (B) Harmony の Generator を利用して Getter, Setter の Delegate 生成
private static Harmony.GetterHandler getShortTermFactor = Harmony.FastAccess.CreateGetterHandler(typeof(StoryWatcher_RampUp).GetField("shortTermFactor", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance));
private static Harmony.SetterHandler setShortTermFactor = Harmony.FastAccess.CreateSetterHandler(typeof(StoryWatcher_RampUp).GetField("shortTermFactor", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance));

static void PostFix(StoryWatcher_RampUp __instance)
{
// (A) Reflection してそのまま 値取得
var foo = field.GetValue(__instance);続き18行
5:
プログラム初心者 ◆l3Flv.uj/A[sage] 2018/08/17(金) 06:03:30
>>4
ありがとうございます!

教えて頂き恐縮なのですが
理解するのに時間が掛かりそうです…
今はブラックボックス的な使い方しかできそうにないです
ですが、おかげで色々なクラスのメソッドを弄れそうです。

自分のプログラミング力が低く
再度お聞きする事になり申し訳ないのですが

(B)の方がおすすめとの事で(B)を試したのですが
'FastAccess'はアクセスできない保護レベルになっています。
とエラーが出てしまいます。

Postfixの場合
呼ばれて 5.000 += 増加分
var foo = field.GetValue(__instance);
…処理後
foo = 5; shortTermFactorField.SetValue(__instance, foo);続き22行
6(1):
名無しさん[sage] 2018/08/17(金) 09:09:51
> 'FastAccess'はアクセスできない保護レベルになっています。
HugsLibがどのバージョンのHarmony使ってるか知りませんが、Harmonyのバージョンが古いんですね。
https://github.com/pardeike/Harmony/commit/cac1bb391f129be8be18237ea9d3607cbc376400#diff-4cf05baf9c04ee551e5e41fcbbca4b8a
ここで、internal から public に変わってます。
V1.1.0以降であれば、使えるっぽいです。あとは、該当メソッドは10行くらいで書けるので自分で同じようなの書くかですね。
https://github.com/pardeike/Harmony/blob/master/Harmony/Extras/FastAccess.cs

> で、再度5.000に戻るという感じで宜しいでしょうか?
たとえば、5以上にはしたくないってだけであれば、Postfix呼ばれた時点では増加分まで入っているので
Postfix内で値を取得して5超過なら5に上書き設定してしまうとか。
if((float)getShortTermFactor(__instance) > 5f)
setShortTermFactor(__instance, 5f);
みたいな感じで。

> Prefixで元のメソッドでスキップが
https://github.com/pardeike/Harmony/wiki/Patching
一番下に
// prefix
// - wants instance, result and count
// - wants to change count続き10行
7:
プログラム初心者 ◆l3Flv.uj/A[sage] 2018/08/18(土) 09:55:35
>>6
お世話になります!
Harmonyを最新に変えたらFastAccessできました。

ログウィンドウを開いた時に表示させるようにしました。
float test = 123;
Log.Message("test :" + test);
と書いたら、デバックログに123と表示されたのでたぶん大丈夫だと思います。

ただの値を見るだけのstatic void postfixメソッドを作り
そして開発者モードで1season進めるコマンドあったので3年位進めて
5000Tickを何回か繰り返してwindowログを開いたら

public void showSrIr(){
Log.Message("sr1 :" + test1);
Log.Message("ir2 :" + test2);
}

sr1:0
sr2:0続き14行
8:
プログラム初心者 ◆l3Flv.uj/A[sage] 2018/08/22(水) 20:19:55
Rimworld B18
net35 Lib.Harmony.1.2.0.1を使用してます。

modを改変しながら毎回初期プレイで21、42日の繰り返しをしていたので遅れました。
あれから色々と試した結果
21日と42日超えてから「5000tick」毎に判定されるので、
デバックによる1Season飛ばしでは飛ばした日数分のirとsr値は上がらないという事が分かりました。
(日数は判定されました)

デバックモードで、虫眼鏡っぽいやつの「Visibility」から「Write Storyteller」で
Tick、Storytellerの人、numRaidsEnemy(襲撃回数)、TotalThreatFactor、ShortFactor、LongFactorが見れる。
これにより正確に現在のTotalThreatFactor、ShortFactor、LongFactorが分かる。
(また、ストーリーテラーの襲撃頻度も分かる)

21日超えてから5000tickで0.000356
42日超えてから5000tickで0.000231上がり出した事から「何らか」のメソッドは正常に動いている

別のクラスのメソッドと「publicなフィールド」で適当に試したら
[HarmonyPatch(typeof(クラス名))]
[HarmonyPatch("メソッド名")]続き24行
9:
名無しさん[sage] 2018/08/22(水) 21:35:37
ちょろっと中見て試してみましたけど、RampUpWatcherTick の Postfix 呼ばれましたよ。
あと、5000tickに1回呼ばれるのではないですよ。
Storywatch_RampUp は毎Tick呼ばれて、その中で値を更新するのが5000Tickに1回なだけなのでPostfixも毎Tick呼ばれます。
そして↑見て気付いたのですけど、"PostFix"ではなく、"Postfix"ですね。fは小文字ですね。
なので、シグネチャ合ってなくて呼ばれないだけではないのでしょうか。
[StaticConstructorOnStartup]
[HarmonyPatch(typeof(StoryWatcher_RampUp), "RampUpWatcherTick")]
class Patch_StoryWatcher_RampUp
{
static Patch_StoryWatcher_RampUp()
{
HarmonyInstance.Create("Nullre.TestMod").PatchAll(Assembly.GetExecutingAssembly());
}

static void Postfix(StoryWatcher_RampUp __instance)
{
Log.Message("ShortTermFactor : " + __instance.ShortTermFactor);
}
}
まぁ、リフレクションとか動的とか解決するに動かす場合によくやるミスですね。静的ならコンパイルエラーで一発で分かりますからね。
10(1):
名無しさん[sage] 2018/08/22(水) 21:41:33
> まぁ、リフレクションとか動的とか解決するに動かす場合によくやるミスですね。静的ならコンパイルエラーで一発で分かりますからね。
まぁ、リフレクションとかで動的に名前解決する場合によくやるミスですね。静的ならコンパイルエラーで一発で分かりますからね。

怪しい日本語ですみません。。。
11:
プログラム初心者 ◆l3Flv.uj/A[sage] 2018/08/22(水) 22:13:24
>>10
あ……ほんとだ、StoryWatcher_RampUpのだけPostFixになっていました
他は全部PostfixになっていたのにそれだけPostFixに…
別クラス別メソッド(public field)でやった時はPostfixになっていたので出来たのですね
直したらLog.Message動きました

いやはやお恥ずかしい

もう一度やり直してきます!
12:
プログラム初心者 ◆l3Flv.uj/A[sage] 2018/08/24(金) 03:47:23
privateのフィールドの書き換えできました!
初めてC#でまともなmodを作れました
ほぼ答えを教えて頂きましたが…
後は調整と改良していこうと思います

また、色んな方のmodの中身を見させて頂き
どのクラスがどんな作用をしているのか徐々に理解出来てきたので
別のmodも作成してみようと思います
13:
プログラム初心者 ◆l3Flv.uj/A[sage] 2018/08/24(金) 05:42:03
Harmonyの英wikiの翻訳を読んでいて___someFieldを使用するとインスタンスフィールドの
読み書きができるみたいですね(スリーアンダースコア)
その時はPost「F」ixだった為に…反映されませんでしたが…

void型ではないのも__resultで戻り値にアクセスできるので
色々とmod作成が捗る…

static void Postfix(StoryWatcher_RampUp __instance,ref float ___shortTermFactor,ref float ___longTermFactor)
{
___shortTermFactor = 8;
___longTermFactor = 8;
}

__instance __result __state ___someField __originalMethod

もうちょっと英wikiの方の熟読が必要かもですね…
辞書抱えて翻訳してきます
14:
プログラム初心者 ◆l3Flv.uj/A[sage] 2018/08/24(金) 06:30:47
namespace Test
{
[StaticConstructorOnStartup]
class Main
{
static Main()
{
var harmony = HarmonyInstance.Create("com.test.fixedvalue");
harmony.PatchAll(Assembly.GetExecutingAssembly());
}
}

class Flag {
public static bool flag = true;
}

[HarmonyPatch(typeof(StoryWatcher_RampUp))]
[HarmonyPatch("RampUpWatcherTick")]
class StoryWatcher_RampUp_RampUpWatcherTick_Patch
{続き18行
15:
名無しさん[sage] 2018/08/25(土) 01:27:18
おめでとうございます!
ぜひ、ある程度形になったら公開してみてください。

> Harmonyの英wikiの翻訳を読んでいて___someFieldを使用するとインスタンスフィールドの
> 読み書きができるみたいですね(スリーアンダースコア)

これは知りませんでした。勉強になりました。
あと、面倒な方法教えちゃってすみませんでした。
02:24:55時点のスレ終端
続き44秒後位に新着取得可
 元スレ:https://jbbs.shitaraba.net/bbs/read.cgi/game/60283/1534322297/