Skip to content

Commit 92743ac

Browse files
committed
Merge branch 'upcoming' of https://github.com/SignatureBeef/Open-Terraria-API into upcoming
2 parents b6a0f59 + c08e479 commit 92743ac

3 files changed

Lines changed: 199 additions & 2 deletions

File tree

OTAPI.Scripts/Mods/HookCommandProcessing.Server.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,18 @@ void HookCommandProcessing(MonoModder modder)
3939
var startDedInputCallBack = modder.GetILCursor(() => Terraria.Main.startDedInputCallBack());
4040

4141
var vText = startDedInputCallBack.Body.Variables[0];
42+
#if TerrariaServer_1450_OrAbove || Terraria__1450_OrAbove || tModLoader_1450_OrAbove
43+
#else
4244
var vTextLowered = startDedInputCallBack.Body.Variables[1];
45+
#endif
4346

4447
if (vText.VariableType.FullName != modder.Module.TypeSystem.String.FullName)
4548
throw new NotSupportedException("Expected the first variable to be string");
49+
#if TerrariaServer_1450_OrAbove || Terraria__1450_OrAbove || tModLoader_1450_OrAbove
50+
#else
4651
if (vTextLowered.VariableType.FullName != modder.Module.TypeSystem.String.FullName)
4752
throw new NotSupportedException("Expected the second variable to be string");
48-
53+
#endif
4954
var exceptionHandler = startDedInputCallBack.Body.ExceptionHandlers.Single(
5055
x => (
5156
x.TryStart.Next.OpCode == OpCodes.Ldstr
@@ -62,8 +67,12 @@ void HookCommandProcessing(MonoModder modder)
6267

6368
exceptionHandler.TryStart.ReplaceTransfer(newStart, startDedInputCallBack.Method);
6469

70+
#if TerrariaServer_1450_OrAbove || Terraria__1450_OrAbove || tModLoader_1450_OrAbove
71+
startDedInputCallBack.EmitDelegate<Func<string, bool>>(OTAPI.Hooks.Main.InvokeCommandProcess);
72+
#else
6573
startDedInputCallBack.Emit(OpCodes.Ldloc, vTextLowered)
6674
.EmitDelegate<Func<string, string, bool>>(OTAPI.Hooks.Main.InvokeCommandProcess);
75+
#endif
6776
startDedInputCallBack.Emit(OpCodes.Brfalse, exceptionHandler.TryEnd.Previous);
6877
}
6978

@@ -92,6 +101,17 @@ public static bool InvokeCommandProcess(string lowered, string raw)
92101
CommandProcess?.Invoke(null, args);
93102
return args.Result != HookResult.Cancel;
94103
}
104+
105+
public static bool InvokeCommandProcess(string raw)
106+
{
107+
var args = new CommandProcessEventArgs()
108+
{
109+
Lowered = raw.ToLower(),
110+
Command = raw,
111+
};
112+
CommandProcess?.Invoke(null, args);
113+
return args.Result != HookResult.Cancel;
114+
}
95115
}
96116
}
97117
}

OTAPI.Scripts/Patches/HookChestQuickStack.Server.cs

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ You should have received a copy of the GNU General Public License
2424
using MonoMod;
2525
using MonoMod.Cil;
2626
using System;
27+
using System.Collections.Generic;
2728
using System.Linq;
29+
using Terraria;
30+
using Terraria.GameContent;
31+
using Terraria.ID;
2832

2933
/// <summary>
3034
/// @doc Creates Hooks.Chest.QuickStack.
@@ -39,7 +43,121 @@ partial class ChestHooks
3943
static void HookChestQuickStack(ModFwModder modder)
4044
{
4145
#if TerrariaServer_1450_OrAbove || Terraria__1450_OrAbove || tModLoader_1450_OrAbove
42-
Console.WriteLine("[TODO] reimplement HookChestQuickStack for 1.4.5+");
46+
{
47+
// DO NOT use GetILCursor(). The instruction body does not have jumps transformed into labels,
48+
// a process which is required to happen for the edits below to work.
49+
var ctx = new ILContext(modder.GetMethodDefinition(() => QuickStacking.BuildDestinationMetricsAndStackItems(default, default, default)));
50+
51+
// Hooks onto quick stacking into existing slot
52+
ctx.Invoke((ctx) =>
53+
{
54+
var csr = new ILCursor(ctx);
55+
56+
ILLabel endLabel = null!;
57+
csr.GotoNext(
58+
MoveType.After,
59+
i => i.MatchLdarg(1),
60+
i => i.MatchLdcI4(1),
61+
i => i.MatchStfld(typeof(QuickStacking.DestinationHelper), nameof(QuickStacking.DestinationHelper.transferBlocked)),
62+
i => i.MatchBr(out endLabel)
63+
);
64+
// AfterLabel is not the same as After + MoveAfterLabels for some reason
65+
csr.MoveAfterLabels();
66+
67+
// Load player ID (source.slots[0].Player.whoAmI)
68+
// source
69+
csr.Emit(OpCodes.Ldarg_0);
70+
// ^.slots
71+
csr.Emit(OpCodes.Ldfld, modder.GetFieldDefinition(() => default(QuickStacking.SourceInventory).slots));
72+
// ^[0]
73+
csr.Emit(OpCodes.Ldc_I4_0);
74+
csr.Emit(OpCodes.Ldelem_Any, modder.GetDefinition<PlayerItemSlotID.SlotReference>());
75+
// ^.Player
76+
csr.Emit(OpCodes.Ldfld, modder.GetFieldDefinition(() => default(PlayerItemSlotID.SlotReference).Player));
77+
// ^.whoAmI
78+
csr.Emit(OpCodes.Ldfld, modder.GetFieldDefinition(() => default(Player)!.whoAmI));
79+
80+
// Load item
81+
// NOTE: this is very fragile, but I can't think of a way to write it better without significantly bloating the code
82+
csr.Emit(OpCodes.Ldloc_S, (byte)3);
83+
84+
// Load chest index
85+
csr.Emit(OpCodes.Ldarg_S, (byte)1);
86+
// ^.ChestIndex (property get)
87+
csr.Emit(OpCodes.Callvirt, modder.GetDefinition<QuickStacking.DestinationHelper>().Properties.Single(p => p.Name == "ChestIndex")!.GetMethod);
88+
89+
// Call hook
90+
csr.Emit(OpCodes.Call, modder.GetMethodDefinition(() => OTAPI.Hooks.Chest.InvokeQuickStack(default, default!, default)));
91+
92+
// Continue if handled
93+
csr.Emit(OpCodes.Brfalse, endLabel);
94+
});
95+
}
96+
{
97+
// used for the out parameter only
98+
List<int> _blockedChests;
99+
100+
// DO NOT use GetILCursor(). The instruction body does not have jumps transformed into labels,
101+
// a process which is required to happen for the edits below to work.
102+
var ctx = new ILContext(modder.GetMethodDefinition(() => QuickStacking.Transfer(default, default, out _blockedChests, default)));
103+
104+
// Hooks onto quick stacking overflowing into a new stack
105+
ctx.Invoke((ctx) =>
106+
{
107+
var csr = new ILCursor(ctx);
108+
int count = 0;
109+
110+
while (csr.TryGotoNext(
111+
MoveType.Before,
112+
i => i.MatchLdarg(0),
113+
i => i.MatchLdloc(out _),
114+
i => i.MatchCall(typeof(QuickStacking), nameof(QuickStacking.Consolidate))
115+
))
116+
{
117+
if (++count > 2)
118+
{
119+
throw new Exception($"More than two matches of {nameof(QuickStacking.Consolidate)}.");
120+
}
121+
122+
// Both targets are preceded by a jump instruction exiting the loop
123+
var endInstruction = csr.Prev.Operand;
124+
125+
csr.MoveAfterLabels();
126+
127+
// Load player ID (source.slots[0].Player.whoAmI)
128+
// source
129+
csr.Emit(OpCodes.Ldarg_0);
130+
// ^.slots
131+
csr.Emit(OpCodes.Ldfld, modder.GetFieldDefinition(() => default(QuickStacking.SourceInventory).slots));
132+
// ^[0]
133+
csr.Emit(OpCodes.Ldc_I4_0);
134+
csr.Emit(OpCodes.Ldelem_Any, modder.GetDefinition<PlayerItemSlotID.SlotReference>());
135+
// ^.Player
136+
csr.Emit(OpCodes.Ldfld, modder.GetFieldDefinition(() => default(PlayerItemSlotID.SlotReference).Player));
137+
// ^.whoAmI
138+
csr.Emit(OpCodes.Ldfld, modder.GetFieldDefinition(() => default(Player)!.whoAmI));
139+
140+
// Load item
141+
// NOTE: this is very fragile, but I can't think of a way to write it better without significantly bloating the code
142+
csr.Emit(OpCodes.Ldloc_S, (byte)(count == 1 ? 6 : 9));
143+
144+
// Load chest index
145+
// NOTE: this is very fragile, but I can't think of a way to write it better without significantly bloating the code
146+
csr.Emit(OpCodes.Ldloc_S, (byte)(count == 1 ? 7 : 10));
147+
// ^.ChestIndex (property get)
148+
csr.Emit(OpCodes.Callvirt, modder.GetDefinition<QuickStacking.DestinationHelper>().Properties.Single(p => p.Name == "ChestIndex")!.GetMethod);
149+
150+
// Call hook
151+
csr.Emit(OpCodes.Call, modder.GetMethodDefinition(() => OTAPI.Hooks.Chest.InvokeQuickStack(default, default!, default)));
152+
153+
// Continue if handled
154+
csr.Emit(OpCodes.Brfalse, endInstruction);
155+
156+
// Move after Consolidate call
157+
csr.Goto(csr.Index + 3);
158+
}
159+
});
160+
}
43161
#else
44162
var csr = modder.GetILCursor(() => Terraria.Chest.PutItemInNearbyChest(null, default));
45163
PutItemInNearbyChest = csr.Method;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
Copyright (C) 2025 sors89
3+
4+
This file is part of Open Terraria API v3 (OTAPI)
5+
6+
This program is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
#if !tModLoader && !tModLoaderServer
20+
using ModFramework;
21+
using Mono.Cecil.Cil;
22+
using MonoMod;
23+
using System;
24+
using System.Linq;
25+
26+
/// <summary>
27+
/// @doc Patch Terraria.Main.rand into a property
28+
/// </summary>
29+
[MonoModIgnore]
30+
partial class Rand
31+
{
32+
[Modification(ModType.PreMerge, "Patching Terraria.Main.rand")]
33+
static void PatchMainRand(ModFwModder modder)
34+
{
35+
var rand = modder.GetFieldDefinition(() => Terraria.Main.rand);
36+
var prop = rand.RemapAsProperty(modder);
37+
38+
var csr = modder.GetILCursor(prop.GetMethod);
39+
40+
/*
41+
0 0000 ldsfld string Terraria.Main::'<rand>k__BackingField'
42+
1 0005 ret
43+
*/
44+
45+
var backingField = modder.Module.GetDefinition<Terraria.Main>().Fields.Where(x => x.Name.Contains("<rand>")).First();
46+
47+
csr.GotoNext(i => i.OpCode == OpCodes.Ret);
48+
csr.Emit(OpCodes.Ldnull);
49+
csr.Emit(OpCodes.Nop);
50+
csr.Emit(OpCodes.Newobj, typeof(Terraria.Utilities.UnifiedRandom).GetConstructor(new Type[] { }));
51+
csr.Emit(OpCodes.Stsfld, backingField);
52+
csr.Emit(OpCodes.Ldsfld, backingField);
53+
54+
var nop = csr.Body.Instructions.Where(i => i.OpCode == OpCodes.Nop).First();
55+
nop.OpCode = OpCodes.Bne_Un_S;
56+
nop.Operand = csr.Previous;
57+
}
58+
}
59+
#endif

0 commit comments

Comments
 (0)