runelite-client: script update / outdated rework (thanks TomC)
This commit is contained in:
@@ -2118,4 +2118,8 @@ public interface Client extends GameShell
|
||||
String getSelectedItemName();
|
||||
|
||||
Widget getMessageContinueWidget();
|
||||
|
||||
void setOutdatedScript(String outdatedScript);
|
||||
|
||||
List<String> getOutdatedScripts();
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
package net.runelite.api;
|
||||
|
||||
import java.util.List;
|
||||
import net.runelite.api.hooks.DrawCallbacks;
|
||||
import java.awt.Canvas;
|
||||
import net.runelite.api.hooks.DrawCallbacks;
|
||||
|
||||
@@ -62,6 +62,7 @@ dependencies {
|
||||
implementation(group = "org.pushing-pixels", name = "radiance-substance", version = "2.5.1")
|
||||
implementation(group = "net.sf.jopt-simple", name = "jopt-simple", version = "5.0.4")
|
||||
implementation(group = "org.apache.commons", name = "commons-text", version = "1.9")
|
||||
implementation(group = "commons-io", name = "commons-io", version = "2.8.0")
|
||||
implementation(group = "org.jetbrains", name = "annotations", version = "20.1.0")
|
||||
implementation(group = "org.jooq", name = "jooq", version = "3.14.0")
|
||||
implementation(group = "org.jooq", name = "jooq-codegen", version = "3.14.0")
|
||||
|
||||
@@ -836,8 +836,6 @@ LABEL729:
|
||||
iload 10
|
||||
iload 11
|
||||
iload 28
|
||||
sconst "addLastRow"
|
||||
runelite_callback
|
||||
iload 29
|
||||
iload 12
|
||||
iload 13
|
||||
@@ -0,0 +1 @@
|
||||
7AA081A68C6157F9B74DDB6B04F00B1691113941EED8996A4DEABF643F45EA25
|
||||
@@ -47,8 +47,19 @@ LABEL23:
|
||||
get_varbit 8119
|
||||
iconst 1
|
||||
if_icmpeq LABEL42
|
||||
jump LABEL146
|
||||
jump LABEL155
|
||||
LABEL42:
|
||||
invoke 3160
|
||||
iconst 1
|
||||
if_icmpeq LABEL46
|
||||
jump LABEL51
|
||||
LABEL46:
|
||||
sconst "<img=22>"
|
||||
chat_playername
|
||||
join_string 2
|
||||
sstore 2
|
||||
jump LABEL59
|
||||
LABEL51:
|
||||
iconst 105
|
||||
iconst 115
|
||||
iconst 1894
|
||||
@@ -57,16 +68,17 @@ LABEL42:
|
||||
chat_playername
|
||||
join_string 2
|
||||
sstore 2
|
||||
LABEL59:
|
||||
iload 4
|
||||
iconst 1
|
||||
if_icmpeq LABEL54
|
||||
jump LABEL58
|
||||
LABEL54:
|
||||
if_icmpeq LABEL63
|
||||
jump LABEL67
|
||||
LABEL63:
|
||||
sload 2
|
||||
sconst "<img=19>"
|
||||
append
|
||||
sstore 2
|
||||
LABEL58:
|
||||
LABEL67:
|
||||
sload 2
|
||||
sconst ": "
|
||||
sload 0
|
||||
@@ -77,9 +89,9 @@ LABEL58:
|
||||
sstore 2
|
||||
iload 2
|
||||
iconst 80
|
||||
if_icmplt LABEL70
|
||||
jump LABEL77
|
||||
LABEL70:
|
||||
if_icmplt LABEL79
|
||||
jump LABEL86
|
||||
LABEL79:
|
||||
sload 2
|
||||
sload 0
|
||||
sconst "*"
|
||||
@@ -87,7 +99,7 @@ LABEL70:
|
||||
join_string 3
|
||||
append
|
||||
sstore 2
|
||||
LABEL77:
|
||||
LABEL86:
|
||||
sload 2
|
||||
iconst 2147483647
|
||||
iconst 495
|
||||
@@ -95,9 +107,9 @@ LABEL77:
|
||||
istore 3
|
||||
iload 4
|
||||
iconst 1
|
||||
if_icmpeq LABEL86
|
||||
jump LABEL103
|
||||
LABEL86:
|
||||
if_icmpeq LABEL95
|
||||
jump LABEL112
|
||||
LABEL95:
|
||||
iconst 60
|
||||
iconst 5
|
||||
iload 3
|
||||
@@ -114,8 +126,8 @@ LABEL86:
|
||||
iconst 2
|
||||
iconst 10616871
|
||||
if_setposition
|
||||
jump LABEL115
|
||||
LABEL103:
|
||||
jump LABEL124
|
||||
LABEL112:
|
||||
iconst 0
|
||||
iconst 30
|
||||
iconst 0
|
||||
@@ -128,26 +140,26 @@ LABEL103:
|
||||
iconst 2
|
||||
iconst 10616871
|
||||
if_setposition
|
||||
LABEL115:
|
||||
LABEL124:
|
||||
iload 3
|
||||
iconst 10616890
|
||||
if_getwidth
|
||||
if_icmpgt LABEL120
|
||||
jump LABEL126
|
||||
LABEL120:
|
||||
if_icmpgt LABEL129
|
||||
jump LABEL135
|
||||
LABEL129:
|
||||
iconst 2
|
||||
iconst 2
|
||||
iconst 0
|
||||
iconst 10616890
|
||||
if_settextalign
|
||||
jump LABEL131
|
||||
LABEL126:
|
||||
jump LABEL140
|
||||
LABEL135:
|
||||
iconst 0
|
||||
iconst 2
|
||||
iconst 0
|
||||
iconst 10616890
|
||||
if_settextalign
|
||||
LABEL131:
|
||||
LABEL140:
|
||||
iconst 10616890
|
||||
if_clearops
|
||||
iconst -1
|
||||
@@ -162,8 +174,19 @@ LABEL131:
|
||||
sconst ""
|
||||
iconst 10616890
|
||||
if_setonop
|
||||
jump LABEL187
|
||||
LABEL146:
|
||||
jump LABEL205
|
||||
LABEL155:
|
||||
invoke 3160
|
||||
iconst 1
|
||||
if_icmpeq LABEL159
|
||||
jump LABEL164
|
||||
LABEL159:
|
||||
sconst "<img=22>"
|
||||
sconst " You must set a name before you can chat."
|
||||
join_string 2
|
||||
sstore 2
|
||||
jump LABEL172
|
||||
LABEL164:
|
||||
iconst 105
|
||||
iconst 115
|
||||
iconst 1894
|
||||
@@ -172,6 +195,7 @@ LABEL146:
|
||||
sconst " You must set a name before you can chat."
|
||||
join_string 2
|
||||
sstore 2
|
||||
LABEL172:
|
||||
iconst 1
|
||||
iconst 2
|
||||
iconst 0
|
||||
@@ -205,7 +229,7 @@ LABEL146:
|
||||
sconst "ii"
|
||||
iconst 10616890
|
||||
if_setonop
|
||||
LABEL187:
|
||||
LABEL205:
|
||||
sload 2
|
||||
iconst 10616890
|
||||
if_settext
|
||||
@@ -53,7 +53,7 @@ LABEL35:
|
||||
istore 4
|
||||
LABEL37:
|
||||
staffmodlevel
|
||||
iconst -1
|
||||
iconst 0
|
||||
if_icmpgt LABEL41
|
||||
jump LABEL43
|
||||
LABEL41:
|
||||
@@ -0,0 +1 @@
|
||||
1CCC672616971AEB7A1E4C00885367CEFC75A873DD22A37BE99652F2DA107653
|
||||
@@ -487,7 +487,7 @@ LABEL437:
|
||||
iconst 73
|
||||
iconst 73
|
||||
iload 1
|
||||
iconst 10551305
|
||||
iconst 10551304
|
||||
enum
|
||||
if_hassub
|
||||
iconst 1
|
||||
@@ -542,7 +542,7 @@ LABEL481:
|
||||
iconst 73
|
||||
iconst 73
|
||||
iload 1
|
||||
iconst 10551305
|
||||
iconst 10551304
|
||||
enum
|
||||
istore 18
|
||||
iconst 0
|
||||
@@ -0,0 +1 @@
|
||||
1EDA8CA79506CC62A192A844B88AC924BAD3060D9E32150C43458E135908329B
|
||||
@@ -11,27 +11,24 @@
|
||||
get_varbit 5314
|
||||
iconst 1
|
||||
if_icmpeq LABEL9
|
||||
jump LABEL17
|
||||
jump LABEL14
|
||||
LABEL9:
|
||||
iconst 111
|
||||
iconst 105
|
||||
iconst 906
|
||||
iconst 94
|
||||
iconst 3
|
||||
inv_getobj
|
||||
enum
|
||||
invoke 3648
|
||||
istore 1
|
||||
LABEL17:
|
||||
LABEL14:
|
||||
iload 1
|
||||
iconst 0
|
||||
if_icmple LABEL21
|
||||
jump LABEL31
|
||||
LABEL21:
|
||||
if_icmple LABEL18
|
||||
jump LABEL28
|
||||
LABEL18:
|
||||
iconst 1 ; What we compare the boolean with
|
||||
iconst 0 ; Boolean
|
||||
sconst "drawSpecbarAnyway"
|
||||
runelite_callback
|
||||
if_icmpeq LABEL41
|
||||
if_icmpeq LABEL38 ; change this
|
||||
iconst 1
|
||||
iconst 38862883
|
||||
if_sethide
|
||||
@@ -42,57 +39,57 @@ LABEL21:
|
||||
iconst 38862850
|
||||
if_setsize
|
||||
return
|
||||
LABEL31:
|
||||
LABEL28:
|
||||
invoke 1972
|
||||
iconst 1
|
||||
if_icmpeq LABEL35
|
||||
jump LABEL41
|
||||
LABEL35:
|
||||
if_icmpeq LABEL32
|
||||
jump LABEL38
|
||||
LABEL32:
|
||||
iconst 190
|
||||
iconst 16
|
||||
iconst 0
|
||||
iconst 0
|
||||
iconst 38862850
|
||||
if_setsize
|
||||
LABEL41:
|
||||
LABEL38:
|
||||
iconst 0
|
||||
istore 2
|
||||
iconst 38862883
|
||||
if_gethide
|
||||
iconst 1
|
||||
if_icmpeq LABEL48
|
||||
jump LABEL50
|
||||
LABEL48:
|
||||
if_icmpeq LABEL45
|
||||
jump LABEL47
|
||||
LABEL45:
|
||||
iconst 1
|
||||
istore 2
|
||||
LABEL50:
|
||||
LABEL47:
|
||||
iconst 0
|
||||
iconst 38862883
|
||||
if_sethide
|
||||
get_varp 301
|
||||
iconst 0
|
||||
if_icmpgt LABEL57
|
||||
jump LABEL61
|
||||
LABEL57:
|
||||
if_icmpgt LABEL54
|
||||
jump LABEL58
|
||||
LABEL54:
|
||||
iconst 16776960
|
||||
iconst 38862888
|
||||
if_setcolour
|
||||
jump LABEL64
|
||||
LABEL61:
|
||||
jump LABEL61
|
||||
LABEL58:
|
||||
iconst 16
|
||||
iconst 38862888
|
||||
if_setcolour
|
||||
LABEL64:
|
||||
LABEL61:
|
||||
get_varp 300
|
||||
istore 3
|
||||
iload 3
|
||||
iconst 0
|
||||
if_icmplt LABEL70
|
||||
jump LABEL72
|
||||
LABEL70:
|
||||
if_icmplt LABEL67
|
||||
jump LABEL69
|
||||
LABEL67:
|
||||
iconst 0
|
||||
istore 3
|
||||
LABEL72:
|
||||
LABEL69:
|
||||
sconst "Special Attack: "
|
||||
iload 3
|
||||
iconst 10
|
||||
@@ -106,31 +103,24 @@ LABEL72:
|
||||
iload 3
|
||||
iload 2
|
||||
invoke 189
|
||||
iconst 38862884
|
||||
iconst 0
|
||||
invoke 835
|
||||
pop_int ; Specbar is fully built here
|
||||
iload 1
|
||||
iconst 0
|
||||
if_icmple RETURN ; Return if the weapon isn't supposed to have a spec
|
||||
jump CONTINUE ; Idk why I'm doing it like this but it's the jagex way
|
||||
RETURN:
|
||||
return
|
||||
CONTINUE:
|
||||
iload 3
|
||||
iload 1
|
||||
if_icmpge LABEL89
|
||||
jump LABEL93
|
||||
LABEL89:
|
||||
if_icmpge LABEL86
|
||||
jump LABEL90
|
||||
LABEL86:
|
||||
iconst 3767611
|
||||
iconst 38862887
|
||||
if_setcolour
|
||||
jump LABEL96
|
||||
LABEL93:
|
||||
jump LABEL93
|
||||
LABEL90:
|
||||
iconst 12907
|
||||
iconst 38862887
|
||||
if_setcolour
|
||||
LABEL96:
|
||||
LABEL93:
|
||||
iconst 38862884
|
||||
iconst 0
|
||||
invoke 835
|
||||
pop_int
|
||||
iconst 94
|
||||
iconst 3
|
||||
inv_getobj
|
||||
@@ -141,11 +131,8 @@ LABEL96:
|
||||
iload 4
|
||||
enum
|
||||
sconst " ("
|
||||
iconst 111
|
||||
iconst 105
|
||||
iconst 906
|
||||
iload 4
|
||||
enum
|
||||
invoke 3648
|
||||
iconst 10
|
||||
div
|
||||
tostring
|
||||
@@ -154,13 +141,13 @@ LABEL96:
|
||||
sstore 0
|
||||
iload 4
|
||||
switch
|
||||
22737: LABEL124
|
||||
22740: LABEL124
|
||||
22743: LABEL124
|
||||
22731: LABEL124
|
||||
22734: LABEL124
|
||||
jump LABEL132
|
||||
LABEL124:
|
||||
22737: LABEL118
|
||||
22740: LABEL118
|
||||
22743: LABEL118
|
||||
22731: LABEL118
|
||||
22734: LABEL118
|
||||
jump LABEL126
|
||||
LABEL118:
|
||||
iconst 111
|
||||
iconst 115
|
||||
iconst 1739
|
||||
@@ -169,64 +156,61 @@ LABEL124:
|
||||
sconst " 5-100% "
|
||||
join_string 2
|
||||
sstore 0
|
||||
LABEL132:
|
||||
LABEL126:
|
||||
get_varbit 5712
|
||||
iconst 0
|
||||
if_icmpeq LABEL136
|
||||
jump LABEL201
|
||||
LABEL136:
|
||||
if_icmpeq LABEL130
|
||||
jump LABEL192
|
||||
LABEL130:
|
||||
iload 4
|
||||
iconst 11235
|
||||
if_icmpeq LABEL152
|
||||
if_icmpeq LABEL146
|
||||
iload 4
|
||||
iconst 20408
|
||||
if_icmpeq LABEL152
|
||||
if_icmpeq LABEL146
|
||||
iload 4
|
||||
iconst 12765
|
||||
if_icmpeq LABEL152
|
||||
if_icmpeq LABEL146
|
||||
iload 4
|
||||
iconst 12768
|
||||
if_icmpeq LABEL152
|
||||
if_icmpeq LABEL146
|
||||
iload 4
|
||||
iconst 12767
|
||||
if_icmpeq LABEL152
|
||||
jump LABEL185
|
||||
LABEL152:
|
||||
if_icmpeq LABEL146
|
||||
jump LABEL176
|
||||
LABEL146:
|
||||
iconst 94
|
||||
iconst 13
|
||||
inv_getobj
|
||||
iconst 11212
|
||||
if_icmpeq LABEL173
|
||||
if_icmpeq LABEL167
|
||||
iconst 94
|
||||
iconst 13
|
||||
inv_getobj
|
||||
iconst 11227
|
||||
if_icmpeq LABEL173
|
||||
if_icmpeq LABEL167
|
||||
iconst 94
|
||||
iconst 13
|
||||
inv_getobj
|
||||
iconst 11228
|
||||
if_icmpeq LABEL173
|
||||
if_icmpeq LABEL167
|
||||
iconst 94
|
||||
iconst 13
|
||||
inv_getobj
|
||||
iconst 11229
|
||||
if_icmpeq LABEL173
|
||||
jump LABEL185
|
||||
LABEL173:
|
||||
if_icmpeq LABEL167
|
||||
jump LABEL176
|
||||
LABEL167:
|
||||
sconst "Descent of Dragons: Deal a double attack with dragon arrows that inflicts up to 50% more damage (minimum damage of 8 per hit). ("
|
||||
iconst 111
|
||||
iconst 105
|
||||
iconst 906
|
||||
iload 4
|
||||
enum
|
||||
invoke 3648
|
||||
iconst 10
|
||||
div
|
||||
tostring
|
||||
sconst "%)"
|
||||
join_string 3
|
||||
sstore 0
|
||||
LABEL185:
|
||||
LABEL176:
|
||||
iconst 526
|
||||
iconst -2147483645
|
||||
iconst -1
|
||||
@@ -242,11 +226,11 @@ LABEL185:
|
||||
sconst "I"
|
||||
iconst 38862883
|
||||
if_setonmouseleave
|
||||
jump LABEL205
|
||||
LABEL201:
|
||||
jump LABEL196
|
||||
LABEL192:
|
||||
iconst -1
|
||||
sconst ""
|
||||
iconst 38862883
|
||||
if_setonmouserepeat
|
||||
LABEL205:
|
||||
LABEL196:
|
||||
return
|
||||
@@ -1 +0,0 @@
|
||||
B840A83E8560C8EB17205E2B98F1D9CD25FBC390562E8A5A437B692EEA6D8F15
|
||||
@@ -1 +0,0 @@
|
||||
70481539CCDB751DD4627EE8F4765D6D5BFD331E32E51B73946913C3CE17D942
|
||||
@@ -1 +0,0 @@
|
||||
3444503072AEE70EEB53938C1FDC826A7530B62BB5FAB65402A22BAB08D7B76D
|
||||
@@ -29,6 +29,7 @@ dependencies {
|
||||
compileOnly(group = "com.google.guava", name = "guava", version = "30.0-jre")
|
||||
compileOnly(group = "javax.inject", name = "javax.inject", version = "1")
|
||||
compileOnly(group = "org.slf4j", name = "slf4j-api", version = "1.7.30")
|
||||
compileOnly(group = "commons-io", name = "commons-io", version = "2.8.0")
|
||||
compileOnly(project(":injection-annotations"))
|
||||
compileOnly(project(":runescape-api"))
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package net.runelite.mixins;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.CharStreams;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.runelite.api.mixins.Copy;
|
||||
import net.runelite.api.mixins.Inject;
|
||||
import net.runelite.api.mixins.Mixin;
|
||||
@@ -15,6 +19,7 @@ import net.runelite.api.overlay.OverlayIndex;
|
||||
import net.runelite.rs.api.RSAbstractArchive;
|
||||
import net.runelite.rs.api.RSArchive;
|
||||
import net.runelite.rs.api.RSClient;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@Mixin(RSAbstractArchive.class)
|
||||
@@ -26,6 +31,9 @@ public abstract class RSAbstractArchiveMixin implements RSAbstractArchive
|
||||
@Inject
|
||||
private boolean overlayOutdated;
|
||||
|
||||
@Inject
|
||||
private Map<String, String> scriptNames;
|
||||
|
||||
@Inject
|
||||
@Override
|
||||
public boolean isOverlayOutdated()
|
||||
@@ -33,6 +41,17 @@ public abstract class RSAbstractArchiveMixin implements RSAbstractArchive
|
||||
return overlayOutdated;
|
||||
}
|
||||
|
||||
private InputStream getResourceAsStream(String resource) {
|
||||
final InputStream in
|
||||
= getContextClassLoader().getResourceAsStream(resource);
|
||||
|
||||
return in == null ? getClass().getResourceAsStream(resource) : in;
|
||||
}
|
||||
|
||||
private ClassLoader getContextClassLoader() {
|
||||
return Thread.currentThread().getContextClassLoader();
|
||||
}
|
||||
|
||||
@SuppressWarnings("InfiniteRecursion")
|
||||
@Copy("takeFile")
|
||||
@Replace("takeFile")
|
||||
@@ -46,6 +65,34 @@ public abstract class RSAbstractArchiveMixin implements RSAbstractArchive
|
||||
return rsData;
|
||||
}
|
||||
|
||||
if (scriptNames == null)
|
||||
try
|
||||
{
|
||||
scriptNames = new HashMap<>();
|
||||
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("scripts/");
|
||||
if (is != null)
|
||||
{
|
||||
List<String> files = IOUtils.readLines(is, Charsets.UTF_8);
|
||||
for (String s : files)
|
||||
{
|
||||
if (s.endsWith(".rs2asm"))
|
||||
continue;
|
||||
|
||||
String scriptName = s.replace(".hash", "");
|
||||
InputStream hashStream = ClassLoader.getSystemClassLoader().getResourceAsStream("scripts/" + scriptName + ".hash");
|
||||
if (hashStream != null)
|
||||
{
|
||||
String scriptHash = (String) IOUtils.readLines(hashStream, Charsets.UTF_8).toArray()[0];
|
||||
scriptNames.put(scriptHash, scriptName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
final Logger log = client.getLogger();
|
||||
final String path = String.format("/runelite/%s/%s", archiveId, groupId);
|
||||
|
||||
@@ -68,8 +115,8 @@ public abstract class RSAbstractArchiveMixin implements RSAbstractArchive
|
||||
// Check if hash is correct first, so we don't have to load the overlay file if it doesn't match
|
||||
if (!overlayHash.equalsIgnoreCase(originalHash))
|
||||
{
|
||||
log.warn("Mismatch in overlaid cache archive hash for {}/{}: {} != {}",
|
||||
archiveId, groupId, overlayHash, originalHash);
|
||||
log.error("Script " + scriptNames.get(overlayHash) + " is invalid, and will not be overlaid. This will break plugin(s)!");
|
||||
client.setOutdatedScript(scriptNames.get(overlayHash));
|
||||
overlayOutdated = true;
|
||||
return rsData;
|
||||
}
|
||||
|
||||
@@ -240,6 +240,9 @@ public abstract class RSClientMixin implements RSClient
|
||||
@Inject
|
||||
private boolean comparingAppearance = false;
|
||||
|
||||
@Inject
|
||||
private List<String> outdatedScripts = new ArrayList<>();
|
||||
|
||||
@Inject
|
||||
@Override
|
||||
public void setPrintMenuActions(boolean yes)
|
||||
@@ -1973,5 +1976,22 @@ public abstract class RSClientMixin implements RSClient
|
||||
client.setMusicTrackBoolean(var5);
|
||||
client.setPcmSampleLength(var0);
|
||||
}
|
||||
|
||||
// this exists because the original got inlined
|
||||
@Inject
|
||||
@Override
|
||||
public void setOutdatedScript(String outdatedScript)
|
||||
{
|
||||
if (!outdatedScripts.contains(outdatedScript))
|
||||
outdatedScripts.add(outdatedScript);
|
||||
}
|
||||
|
||||
// this exists because the original got inlined
|
||||
@Inject
|
||||
@Override
|
||||
public List<String> getOutdatedScripts()
|
||||
{
|
||||
return this.outdatedScripts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ dependencies {
|
||||
tasks {
|
||||
register<JavaExec>("assembleMojo") {
|
||||
outputs.cacheIf { true }
|
||||
val inp = "${project.extra["rootPath"]}/runelite-client/src/main/scripts"
|
||||
val inp = "${project.extra["rootPath"]}/runelite-client/src/main/resources/scripts"
|
||||
val out = "${project.extra["rootPath"]}/runelite-client/build/scripts/runelite"
|
||||
inputs.dir(inp)
|
||||
outputs.dir(out)
|
||||
|
||||
Reference in New Issue
Block a user