Skip to content

Commit b2a86af

Browse files
committed
0.4.0
1 parent 159a5cb commit b2a86af

26 files changed

Lines changed: 545 additions & 62 deletions

README.md

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,29 @@
1-
# Embedded Compute rMod
2-
This mod adds the "embedded computer" and related things
1+
# Embedded Computer Mod
2+
[![Modrinth Downloads](https://img.shields.io/badge/dynamic/json?labelColor=black&color=grey&label=&suffix=%20downloads&query=downloads&url=https://api.modrinth.com/v2/project/X41LBcSD&style=flat&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAJPUExURQAAABvZahWnUha1WAYzGQlHIxvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZahvZav///9ScwmYAAADDdFJOUwAAAAAAAA8zW3uOYwIBK3rB6Pn+ml18KiGL5HEDquOIH07R/UzKz2zu+uLHIibtafWkVCMNBRqg7/RQuT8EQbvT+5ETDBSU/NAgCSdZlcQKii7mtxJY5fF/7D1SRkB+EcWh4UilOOtPMdTCR1PqN969vmGDCw7G4DSSsAcGHrSPr3bds5CEwDKoXumcZdwcG4KmjayX32A79pjOqRjIFoXynVYVgPi6qxDnL78p2obVJFquNbGZCPdyvHPZ1yhuh8s+iRzcsrEAAAABYktHRMQUDBvhAAAAB3RJTUUH5wQXDwgZWDUtiQAAAqRJREFUOMttU/k7lFEU/k4L4hsxtNAnhRgiJZOiSfbI2oJpmRFCi2kV0aaNVLTIEpVISmnf97r/WO+534yZnqfzw73vOe+559xz7rmKIoVYvLx95vj6qUSqVBW3sGrwnxsQaAwSwcDz5i9g0wxPfmFIqJCyiEgLE4vDl7iD8PGlETotIqOQahlAdJTLA5sWY5JsbNzysHiihEBWViRqRDOlgyFmJVtMq5JWJ5vhvyZlLevrEmUILKl8PihtvYWcYt6QLmNwFjjEb4SSkZnlZLO5yJzcPBg35bODwZfPZxYwWbC5sKjYzwBUkssxSg1wKNsCtHUb89vTy3GwopBxDmcJ9YdDJYB1B9t2WmUtcbtkqt18Mxsp9irsezS2VEu+uka/mqUWSrFd2VsnhLEetoZ9TFv3HwCexdUdjBWi0aH4YTuEvtoPM3/EO1nnuTpEjD2qHIM54DhR0wmA5hZySzIntyknsbZyi8IB0tp07tRpBDKfgeWsdDjHDlFGRIjnl3O0t573gcMFUBeVS1gvlxB1dOZxv0pIu9IF01UDZRdjr1SuZQhxvbvnRrmsMePmrdsrGPRaKP8OLtmn9KM7A4N3xb8yNExUb5RlZt2btpZbXajzPoYkBaDKrtADp3HkYdloo56ndgx37UcHxSP0tIWBGH9cg1nx6TJFTNhS7eCfcA1PJ+GgDbLDs2GuX3V05Ohj0xYSBGumyq/yfApw4EWTRxPJqxQvIF6+ks9Gr0d4nN+8Nbhoi+NdJEwVSc6hVNvHOUvg+5qeDxoP3GQz6x8/qXLuZ6Op7SPy+gNTnydSELabk1Z8Mbs/hlr4dbobfUgxJMS3UdXza9H33jqnA3/OH41FCZ7/l7HW8vOXiWc9GvPf06D953/T2O/EP8HBNtcH0Zm/lqFNUgTAex4AAAAldEVYdGRhdGU6Y3JlYXRlADIwMjMtMDQtMjNUMTU6MDg6MjQrMDA6MDAE5dOaAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIzLTA0LTIzVDE1OjA4OjI0KzAwOjAwdbhrJgAAACh0RVh0ZGF0ZTp0aW1lc3RhbXAAMjAyMy0wNC0yM1QxNTowODoyNSswMDowMITaQU0AAAAASUVORK5CYII=)](https://modrinth.com/mod/embeddedcomputer)
3+
## About
4+
The "Embedded Computer" mod is an addon for CC: Tweaked that adds the Embedded Computer, a secure headless computer, and other goodies to the game.
35

4-
# License
6+
## Features
7+
- The Embedded Computer, it is a secure headless computer that can only be modified by booting it off of a disk drive. It can be wiped via peripheral API (`embeddedPeriph.format()`) and can be "locked" via `embedded.setPassword("superSecurePassword")`, preventing formatting until unlocked with `embeddedPeriph.unlock("superSecurePassword")`
8+
- Higher capacity storage items, the Zip Drive and Flash Card. They can help store large programs for a much higher cost than a normal floppy
9+
- Hard drive block, they allow you to mount a 25mb storage device to ANY directory. You can mount them to `/a` or even a `/mnt/hdd1` at the cost of needing to manually call `hddPeriph.mount("/directory/goes/here")` every boot and `hddPeriph.unmount("/directory/goes/here")` when you want to unmount them.
510

6-
All versions prior to [v2.2 (82b367be5ee7d6888882f9b947e964fa6b011fbc)](https://github.com/WindClan/EmbeddedComputerMod/commit/82b367be5ee7d6888882f9b947e964fa6b011fbc) are under the MIT license
11+
## Versioning
12+
13+
(game version).(big update).(small update)
14+
15+
16+
- 1.20.1 is 0.X.X (you are here)
17+
- 1.21.1 is 1.X.X (latest, no dedicated branch atm)
18+
19+
## Disclaimer
20+
21+
PLEASE DO NOT USE ACTUAL PASSWORDS TO LOCK EMBEDDED COMPUTERS!!! They aren't hashed since setPassword is implemented in Lua and I didn't want to add a random hash lib to rom!!! If you want a secure lock code thats secure you should write a 2kb arbitrary string to `/.LOCKED`
22+
23+
If you use an actual password (even if it was hashed) the server owner could add something to intercept and steal your password! Don't use an actual password you use for other things!!!!
24+
25+
## License
26+
27+
All versions prior to [v0.2.2 (82b367be5ee7d6888882f9b947e964fa6b011fbc)](https://github.com/WindClan/EmbeddedComputerMod/commit/82b367be5ee7d6888882f9b947e964fa6b011fbc) are under the MIT license
728

829
With permission from the other contributor Herr Katze, all versions past that are licensed as MPL 2.0

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ minecraft_version=1.20.1
66
yarn_mappings=1.20.1+build.10
77
loader_version=0.16.10
88
# Mod Properties
9-
mod_version=0.3.0
10-
maven_group=org.featherwhisker
9+
mod_version=0.4.0
10+
maven_group=org.windclan
1111
archives_base_name=embeddedcomputer
1212
# Dependencies
1313
# check this on https://modmuss50.me/fabric.html

src/main/java/org/windclan/embeddedcomputer/ComputerComponents.java

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/main/java/org/windclan/embeddedcomputer/embedded/EmbeddedComputerPeripheral.java

Lines changed: 83 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
*/
66
package org.windclan.embeddedcomputer.embedded;
77

8-
import dan200.computercraft.api.ComputerCraftAPI;
98
import dan200.computercraft.api.filesystem.WritableMount;
109
import dan200.computercraft.api.lua.LuaFunction;
1110
import dan200.computercraft.api.peripheral.IComputerAccess;
@@ -14,20 +13,13 @@
1413
import dan200.computercraft.shared.computer.core.ServerComputer;
1514
import net.minecraft.block.entity.BlockEntity;
1615
import net.minecraft.util.math.Direction;
17-
import org.windclan.embeddedcomputer.storage.harddrive.HardDrivePeripheral;
1816
import org.jetbrains.annotations.Nullable;
1917
import org.windclan.embeddedcomputer.embedded.block.EmbeddedComputerBlockEntity;
18+
import org.windclan.embeddedcomputer.secure.HashUtil;
2019

21-
import java.nio.ByteBuffer;
2220
import java.nio.channels.SeekableByteChannel;
23-
import java.nio.charset.StandardCharsets;
24-
import java.nio.file.OpenOption;
25-
import java.nio.file.StandardOpenOption;
26-
import java.util.Collections;
2721
import java.util.Scanner;
28-
import java.util.Set;
2922

30-
import static dan200.computercraft.shared.pocket.items.PocketComputerItem.getServerComputer;
3123
import static java.util.Objects.isNull;
3224
import static org.windclan.embeddedcomputer.main.log;
3325

@@ -62,19 +54,28 @@ public final boolean isOn() {
6254
var comp1= getServerComp();
6355
return !isNull(comp1) && comp1.isOn();
6456
}
57+
58+
@LuaFunction
59+
public final int getId() {
60+
var comp1= getServerComp();
61+
if (!isNull(comp1)) return comp1.getID();
62+
else return -1;
63+
}
64+
6565
@LuaFunction
6666
public final void reboot() {
6767
var comp1 = getServerComp();
6868
if (!isNull(comp1)) comp1.reboot();
6969
}
70+
7071
@LuaFunction(mainThread = true)
7172
public final boolean format() {
7273
var comp1 = getServerComp();
7374
WritableMount mnt;
7475
if (!isNull(comp1)) {
7576
try {
7677
mnt = comp1.createRootMount();
77-
if (mnt.exists(".LOCKED")) {
78+
if (mnt.exists(".LOCKED") || mnt.exists(".LOCKED_HASHED")) {
7879
return false;
7980
}
8081
try {
@@ -94,7 +95,7 @@ public final boolean format() {
9495
}
9596

9697
@LuaFunction(mainThread = true)
97-
public final void unlock(String pass1) {
98+
public final void unlockPlainText(String pass1) {
9899
ServerComputer comp1 = getServerComp();
99100
if (!isNull(comp)) {
100101
WritableMount mnt;
@@ -132,6 +133,77 @@ public final void unlock(String pass1) {
132133
}
133134
}
134135

136+
@LuaFunction(mainThread = true)
137+
public final void unlockHashed(String pass1,String hashType) {
138+
ServerComputer comp1 = getServerComp();
139+
String hashedPass;
140+
if (hashType.equalsIgnoreCase("sha256")) {
141+
hashedPass = HashUtil.hashStrSHA256(pass1);
142+
for (int i=0; i <= pass1.length(); i++) {
143+
hashedPass = HashUtil.hashStrSHA256(hashedPass);
144+
}
145+
} else if (hashType.equalsIgnoreCase("murmur3")) {
146+
hashedPass = HashUtil.hashStrMurmur3(pass1);
147+
for (int i=0; i <= pass1.length(); i++) {
148+
hashedPass = HashUtil.hashStrMurmur3(hashedPass);
149+
}
150+
} else if (hashType.equalsIgnoreCase("adler32")) {
151+
hashedPass = HashUtil.hashStrAdler32(pass1);
152+
for (int i=0; i <= pass1.length(); i++) {
153+
hashedPass = HashUtil.hashStrAdler32(hashedPass);
154+
}
155+
} else if (hashType.equalsIgnoreCase("siphash24")) {
156+
hashedPass = HashUtil.hashStrSipHash24(pass1);
157+
for (int i=0; i <= pass1.length(); i++) {
158+
hashedPass = HashUtil.hashStrSipHash24(hashedPass);
159+
}
160+
} else {
161+
return;
162+
}
163+
for (int i=0; i <= pass1.length(); i++) {
164+
hashedPass = HashUtil.hashStrSHA512(hashedPass);
165+
}
166+
if (!isNull(comp)) {
167+
WritableMount mnt;
168+
SeekableByteChannel root = null;
169+
Scanner scan = null;
170+
try {
171+
mnt = comp1.createRootMount();
172+
if (mnt.exists(".LOCKED_HASHED")) {
173+
String pass = "";
174+
root = comp1.createRootMount().openForRead(".LOCKED_HASHED");
175+
scan = new Scanner(root);
176+
while (scan.hasNext()) {
177+
pass+=scan.next();
178+
}
179+
scan.close();
180+
if (!pass.equals(hashedPass)) {
181+
root.close();
182+
return;
183+
}
184+
mnt.delete(".LOCKED_HASHED");
185+
return;
186+
}
187+
} catch (Exception ex) {
188+
log.warn(ex.toString());
189+
if (!isNull(root)) {
190+
try {
191+
root.close();
192+
} catch (Exception ignored) {}
193+
}
194+
if (!isNull(scan)) {
195+
scan.close();
196+
}
197+
return;
198+
}
199+
}
200+
}
201+
202+
@LuaFunction(mainThread = true)
203+
public final void unlock(String pass1) {
204+
unlockHashed(pass1,"sha256");
205+
}
206+
135207
// Generic functions
136208
@Override
137209
public void attach(IComputerAccess computer) {}

src/main/java/org/windclan/embeddedcomputer/embedded/block/EmbeddedComputerBlockEntity.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
import dan200.computercraft.shared.computer.core.ComputerFamily;
1111
import dan200.computercraft.shared.computer.core.ServerComputer;
1212
import net.minecraft.block.BlockState;
13-
import net.minecraft.nbt.NbtCompound;
13+
import net.minecraft.block.entity.BlockEntityType;
1414
import net.minecraft.server.world.ServerWorld;
1515
import net.minecraft.util.math.BlockPos;
16-
import org.windclan.embeddedcomputer.ComputerComponents;
1716
import org.windclan.embeddedcomputer.embedded.EmbeddedComputerBrain;
1817
import org.windclan.embeddedcomputer.embedded.EmbeddedComputerPeripheral;
1918
import org.windclan.embeddedcomputer.embedded.ServerEmbeddedComputer;
@@ -25,18 +24,25 @@
2524

2625
public class EmbeddedComputerBlockEntity extends ComputerBlockEntity {
2726
private @Nullable IPeripheral p;
27+
public EmbeddedComputerBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) {
28+
super(type,pos,state,ComputerFamily.ADVANCED);
29+
}
2830
public EmbeddedComputerBlockEntity(BlockPos pos, BlockState state) {
2931
super(registry.EMBEDDED_COMPUTER_ENTITY,pos,state,ComputerFamily.ADVANCED);
3032
}
3133
private final EmbeddedComputerBrain brain = new EmbeddedComputerBrain(this); // This does nothing. it's just there to make the Computer Component happy lmao
34+
public EmbeddedComputerBrain getBrain() {
35+
return brain;
36+
}
3237
@Override
3338
protected ServerComputer createComputer(int id) {
3439
return new ServerEmbeddedComputer(
3540
(ServerWorld) getWorld(), getPos(), //id, label,brain
3641
ServerEmbeddedComputer.properties(id,ComputerFamily.ADVANCED)
3742
.label(label)
3843
.terminalSize(10,3)
39-
.addComponent(ComputerComponents.EMBEDDED,brain)
44+
.addComponent(registry.SECURE_COMPONENT,brain)
45+
.addComponent(registry.EMBEDDED_COMPONENT,brain)
4046
);
4147
}
4248
@Override

src/main/java/org/windclan/embeddedcomputer/main.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.windclan.embeddedcomputer.embedded.EmbeddedComputerAPI;
1111
import org.slf4j.Logger;
1212
import org.slf4j.LoggerFactory;
13+
import org.windclan.embeddedcomputer.secure.SecureComputerAPI;
1314

1415
public class main implements ModInitializer {
1516

@@ -20,8 +21,12 @@ public void onInitialize() {
2021
a.registerPeripherals();
2122
a.registerItemGroups();
2223
ComputerCraftAPI.registerAPIFactory(computer -> {
23-
var embedded = computer.getComponent(ComputerComponents.EMBEDDED);
24+
var embedded = computer.getComponent(registry.EMBEDDED_COMPONENT);
2425
return embedded == null ? null : new EmbeddedComputerAPI(embedded);
2526
});
27+
ComputerCraftAPI.registerAPIFactory(computer -> {
28+
var secure = computer.getComponent(registry.SECURE_COMPONENT);
29+
return secure == null ? null : new SecureComputerAPI(secure);
30+
});
2631
}
2732
}

src/main/java/org/windclan/embeddedcomputer/platform/registry1.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public void registerItemGroups() {
2626
Registry.register(Registries.ITEM_GROUP,itemGroupKey, EMBEDDED_COMPUTER_GROUP);
2727
ItemGroupEvents.modifyEntriesEvent(itemGroupKey).register(itemGroup -> {
2828
itemGroup.add(registry.EMBEDDED_COMPUTER_ITEM);
29+
itemGroup.add(registry.SECURE_COMPUTER_ITEM);
2930
itemGroup.add(registry.HARD_DRIVE_ITEM);
3031
itemGroup.add(registry.DEBUG_MEDIA_ITEM);
3132
itemGroup.add(registry.ZIP_DISK_ITEM);

src/main/java/org/windclan/embeddedcomputer/registry.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
package org.windclan.embeddedcomputer;
77

8+
import dan200.computercraft.api.component.ComputerComponent;
89
import dan200.computercraft.api.peripheral.PeripheralLookup;
910

1011
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
@@ -21,9 +22,13 @@
2122
import net.minecraft.util.Identifier;
2223

2324
import org.windclan.embeddedcomputer.embedded.EmbeddedComputerPeripheral;
25+
import org.windclan.embeddedcomputer.embedded.IEmbeddedComputer;
2426
import org.windclan.embeddedcomputer.embedded.block.EmbeddedComputerBlock;
2527
import org.windclan.embeddedcomputer.embedded.block.EmbeddedComputerBlockEntity;
2628
import org.windclan.embeddedcomputer.embedded.item.ComputerBlockItem;
29+
import org.windclan.embeddedcomputer.secure.SecureComputerPeripheral;
30+
import org.windclan.embeddedcomputer.secure.block.SecureComputerBlock;
31+
import org.windclan.embeddedcomputer.secure.block.SecureComputerBlockEntity;
2732
import org.windclan.embeddedcomputer.storage.harddrive.HardDriveBlock;
2833
import org.windclan.embeddedcomputer.storage.harddrive.HardDriveBlockEntity;
2934
import org.windclan.embeddedcomputer.platform.registry1;
@@ -62,13 +67,33 @@ public class registry {
6267
new HardDriveItem(HARD_DRIVE, new FabricItemSettings().maxCount(1))
6368
);
6469

70+
public static Block SECURE_COMPUTER = Registry.register(
71+
Registries.BLOCK,
72+
new Identifier("embeddedcomputer","secure_computer"),
73+
new SecureComputerBlock<>(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.IGNORE).solid())
74+
);
75+
public static BlockEntityType<SecureComputerBlockEntity> SECURE_COMPUTER_ENTITY = Registry.register(
76+
Registries.BLOCK_ENTITY_TYPE,
77+
new Identifier("embeddedcomputer", "secure_computer_entity"),
78+
BlockEntityType.Builder.create(SecureComputerBlockEntity::new, SECURE_COMPUTER).build(null)
79+
);
80+
public static Item SECURE_COMPUTER_ITEM = Registry.register(
81+
Registries.ITEM,
82+
new Identifier("embeddedcomputer", "secure_computer"),
83+
new ComputerBlockItem(SECURE_COMPUTER)
84+
);
85+
6586
public static final Item DEBUG_MEDIA_ITEM = Registry.register(Registries.ITEM, Identifier.of("embeddedcomputer", "debug_rock"), new DebugMediaItem(new Item.Settings()));
6687
public static final Item ZIP_DISK_ITEM = Registry.register(Registries.ITEM, Identifier.of("embeddedcomputer", "zip_disk"), new ZipDiskItem(new Item.Settings()));
6788
public static final Item FLASH_CARD_ITEM = Registry.register(Registries.ITEM, Identifier.of("embeddedcomputer", "flash_card"), new FlashCardItem(new Item.Settings()));
6889

90+
public static final ComputerComponent<IEmbeddedComputer> EMBEDDED_COMPONENT = ComputerComponent.create("embeddedcomputer", "embedded");
91+
public static final ComputerComponent<IEmbeddedComputer> SECURE_COMPONENT = ComputerComponent.create("embeddedcomputer","secure");
92+
6993
public void registerPeripherals() {
7094
PeripheralLookup.get().registerForBlockEntities(EmbeddedComputerPeripheral::getPeripheral,EMBEDDED_COMPUTER_ENTITY);
7195
PeripheralLookup.get().registerForBlockEntities(HardDrivePeripheral::getPeripheral,HARD_DRIVE_ENTITY);
96+
PeripheralLookup.get().registerForBlockEntities(SecureComputerPeripheral::getPeripheral,SECURE_COMPUTER_ENTITY);
7297
}
7398
public void registerItemGroups() {
7499
var a = new registry1();
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
package org.windclan.embeddedcomputer.secure;
7+
import com.google.common.hash.HashFunction;
8+
import com.google.common.hash.Hashing;
9+
import java.nio.charset.StandardCharsets;
10+
public class HashUtil {
11+
public static String hashStrSHA256(String str) {
12+
HashFunction hash = Hashing.sha256();
13+
return hash.hashString(str, StandardCharsets.ISO_8859_1).toString();
14+
}
15+
public static String hashStrSHA512(String str) {
16+
HashFunction hash = Hashing.sha512();
17+
return hash.hashString(str, StandardCharsets.ISO_8859_1).toString();
18+
}
19+
public static String hashStrMurmur3(String str) {
20+
HashFunction hash = Hashing.murmur3_128();
21+
return hash.hashString(str, StandardCharsets.ISO_8859_1).toString();
22+
}
23+
public static String hashStrAdler32(String str) {
24+
HashFunction hash = Hashing.adler32();
25+
return hash.hashString(str, StandardCharsets.ISO_8859_1).toString();
26+
}
27+
public static String hashStrSipHash24(String str) {
28+
HashFunction hash = Hashing.sipHash24();
29+
return hash.hashString(str, StandardCharsets.ISO_8859_1).toString();
30+
}
31+
}

0 commit comments

Comments
 (0)