Skip to content

Commit c184b9e

Browse files
committed
Can change settings now
1 parent 4998c9a commit c184b9e

8 files changed

Lines changed: 263 additions & 20 deletions

File tree

src/client/java/com/hanprogramer/androids/client/screen/android/SettingsTab.java

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package com.hanprogramer.androids.client.screen.android;
22

3+
import com.hanprogramer.androids.AndroidsCraft;
34
import com.hanprogramer.androids.client.screen.base.BaseScreenTab;
45
import com.hanprogramer.androids.client.screen.widgets.ColoredButtonWidget;
56
import com.hanprogramer.androids.entities.android.AndroidScriptProperty;
7+
import com.hanprogramer.androids.network.AndroidC2SSetPropertyPayload;
8+
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
9+
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
610
import net.minecraft.client.MinecraftClient;
711
import net.minecraft.client.gl.RenderPipelines;
812
import net.minecraft.client.gui.DrawContext;
@@ -12,6 +16,8 @@
1216
import net.minecraft.client.gui.widget.ButtonWidget;
1317
import net.minecraft.client.gui.widget.ElementListWidget;
1418
import net.minecraft.item.ItemStack;
19+
import net.minecraft.network.PacketByteBuf;
20+
import net.minecraft.network.packet.CustomPayload;
1521
import net.minecraft.text.Text;
1622
import net.minecraft.util.Colors;
1723
import net.minecraft.util.Identifier;
@@ -28,22 +34,33 @@ public class SettingsTab extends BaseScreenTab<AndroidInventoryHandledScreen> {
2834
public class SettingsEntry extends ElementListWidget.Entry<SettingsEntry> {
2935
private final Text title;
3036
private final ButtonWidget button;
31-
private final String id;
3237
private Object value;
38+
private AndroidScriptProperty property;
3339

34-
public SettingsEntry(Text title, String entryType, String id, Object value) {
35-
this.title = title;
36-
this.id = id;
37-
this.value = value;
38-
if (Objects.equals(entryType, AndroidScriptProperty.TYPE_BOOLEAN)) {
40+
public SettingsEntry(AndroidScriptProperty property) {
41+
this.property = property;
42+
this.title = Text.of(property.displayName);
43+
this.value = property.value;
44+
if (Objects.equals(property.type, AndroidScriptProperty.TYPE_BOOLEAN)) {
3945
var bool = (Boolean) value;
40-
button = new ColoredButtonWidget(0,0,0,0, bool ? Text.of("ON") : Text.of("OFF"), button1 -> {
41-
System.out.println("Clicked");
46+
button = new ColoredButtonWidget(0,0,0,0, Boolean.TRUE.equals(bool) ? Text.of("ON") : Text.of("OFF"), button1 -> {
47+
// Get the value
48+
var bool2 = !((Boolean) this.value);
49+
this.value = bool2;
50+
51+
// Set title & color
52+
button1.setMessage(bool2 ? Text.of("ON") : Text.of("OFF"));
53+
((ColoredButtonWidget)button1).textColor = bool2? Colors.GREEN : Colors.RED;
54+
55+
// Send update to server
56+
property.value = bool2;
57+
ClientPlayNetworking.send(new AndroidC2SSetPropertyPayload(property, SettingsTab.this.parent.entity.getId()));
58+
4259
}, bool? Colors.GREEN : Colors.RED);
4360
}
44-
else if (Objects.equals(entryType, AndroidScriptProperty.TYPE_BLOCKPOS)) {
61+
else if (Objects.equals(property.type, AndroidScriptProperty.TYPE_BLOCKPOS)) {
4562
var val = (BlockPos) value;
46-
button = ButtonWidget.builder(Text.of(val.toString()), button1 -> {
63+
button = ButtonWidget.builder(Text.of(String.format("[%s,%s,%s]", val.getX(), val.getY(), val.getZ())), button1 -> {
4764
System.out.println("Clicked");
4865
}).build();
4966
}
@@ -118,7 +135,7 @@ public int getRowWidth() {
118135
};
119136
list.setDimensionsAndPosition(160, 150, x + 8, y + 8);
120137
for (var p : parent.entity.getProperties()) {
121-
list.children().add(new SettingsEntry(Text.of(p.displayName), p.type, p.id, p.value));
138+
list.children().add(new SettingsEntry(p));
122139
}
123140
elements.add(list);
124141
}
@@ -142,4 +159,5 @@ public void drawBackground(DrawContext context, float delta, int mouseX, int mou
142159
int j = (this.parent.height - this.parent.backgroundHeight) / 2;
143160
context.drawTexture(RenderPipelines.GUI_TEXTURED, TEXTURE, i, j, 0.0F, 0.0F, this.parent.backgroundWidth, this.parent.backgroundHeight, 256, 256);
144161
}
162+
145163
}

src/client/java/com/hanprogramer/androids/client/screen/widgets/ColoredButtonWidget.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
public class ColoredButtonWidget extends ButtonWidget {
1515
private static final ButtonTextures TEXTURES = new ButtonTextures(Identifier.ofVanilla("widget/button"), Identifier.ofVanilla("widget/button_disabled"), Identifier.ofVanilla("widget/button_highlighted"));
16-
private final int textColor;
16+
public int textColor;
1717

1818
public ColoredButtonWidget(int x, int y, int width, int height, Text message, PressAction onPress, int textColor) {
1919
super(x, y, width, height, message, onPress, Supplier::get);

src/main/java/com/hanprogramer/androids/AndroidsCraft.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.hanprogramer.androids.blocks.computer.ComputerBlockEntity;
44
import com.hanprogramer.androids.blocks.computer.ComputerScreenHandler;
55
import com.hanprogramer.androids.entities.android.AndroidEntity;
6+
import com.hanprogramer.androids.network.AndroidC2SSetPropertyPayload;
67
import com.hanprogramer.androids.network.ComputerOSInitPacketS2CPayload;
78
import com.hanprogramer.androids.network.ComputerOSUpdateC2SPayload;
89
import com.hanprogramer.androids.network.ModPackets;
@@ -23,7 +24,6 @@
2324
import static com.hanprogramer.androids.ModEntities.ANDROID;
2425
import static com.hanprogramer.androids.entities.android.AndroidEntity.ANDROID_MATERIAL_TRACKED_DATA_HANDLER;
2526
import static com.hanprogramer.androids.entities.android.AndroidEntity.ANDROID_PROPERTIES_TRACKED_DATA_HANDLER;
26-
import static com.hanprogramer.androids.entities.android.util.PropertyListDataHandler.PROPERTY_LIST_DATA_HANDLER;
2727

2828
public class AndroidsCraft implements ModInitializer {
2929
public static final String MOD_ID = "androids";
@@ -55,6 +55,23 @@ public void onInitialize() {
5555
});
5656
});
5757

58+
PayloadTypeRegistry.playC2S().register(AndroidC2SSetPropertyPayload.ID, AndroidC2SSetPropertyPayload.PACKET_CODEC);
59+
ServerPlayNetworking.registerGlobalReceiver(AndroidC2SSetPropertyPayload.ID, (payload, context) -> {
60+
var prop = payload.property();
61+
var entityId = payload.entityId();
62+
var ent = context.player().getWorld().getEntityById(entityId);
63+
64+
if (ent != null) {
65+
if (ent instanceof AndroidEntity android) {
66+
try {
67+
android.applyProperty(prop);
68+
} catch (Exception e) {
69+
System.out.println(e); // TODO: Log this somewhere
70+
}
71+
}
72+
}
73+
});
74+
5875

5976
FabricTrackedDataRegistry.register(Identifier.of("androids:material_data_handler"), ANDROID_MATERIAL_TRACKED_DATA_HANDLER);
6077
FabricTrackedDataRegistry.register(Identifier.of("androids:android_property_list"), ANDROID_PROPERTIES_TRACKED_DATA_HANDLER); // do this once during mod initialization

src/main/java/com/hanprogramer/androids/entities/android/AndroidEntity.java

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.hanprogramer.androids.AndroidsCraft;
44
import com.hanprogramer.androids.entities.android.inventory.TrackedInventory;
5-
import com.hanprogramer.androids.entities.android.scripting.BlockArea;
65
import com.hanprogramer.androids.entities.android.scripting.SelfBridge;
76
import com.hanprogramer.androids.entities.android.scripting.Sleeper;
87
import com.hanprogramer.androids.entities.android.scripting.WorldBridge;
@@ -37,7 +36,6 @@
3736
import net.minecraft.text.Text;
3837
import net.minecraft.util.ActionResult;
3938
import net.minecraft.util.Hand;
40-
import net.minecraft.util.math.BlockPos;
4139
import net.minecraft.util.math.Box;
4240
import net.minecraft.world.World;
4341
import org.graalvm.polyglot.*;
@@ -48,6 +46,7 @@
4846

4947
import java.util.ArrayList;
5048
import java.util.List;
49+
import java.util.Objects;
5150
import java.util.concurrent.CancellationException;
5251
import java.util.concurrent.CompletableFuture;
5352

@@ -389,7 +388,7 @@ public boolean isOn() {
389388
}
390389

391390
public void setOn(boolean value) {
392-
if(getWorld().isClient) return;
391+
if (getWorld().isClient) return;
393392
if (dataTracker.get(totalPower) <= 0)
394393
value = false;
395394

@@ -421,7 +420,7 @@ public void setOn(boolean value) {
421420
isWaitingStart = false;
422421
startFnFuture = null;
423422
if (err != null) {
424-
if(err instanceof CancellationException) return;
423+
if (err instanceof CancellationException) return;
425424
LOG.error("Error in JS start() for {}", this, err);
426425
}
427426
};
@@ -469,7 +468,7 @@ public void tick() {
469468
isWaitingTick = false;
470469
tickFnFuture = null;
471470
if (err != null) {
472-
if(err instanceof CancellationException) return;
471+
if (err instanceof CancellationException) return;
473472
LOG.error("Error in JS tick() for {}", this, err);
474473
}
475474
};
@@ -586,7 +585,7 @@ public void exposeVar(String name, String displayName, Object value, String type
586585
// throw new Exception("Unknown variable type: " + value);
587586
// }
588587
_properties.add(new AndroidScriptProperty(name, displayName, type, value));
589-
dataTracker.set(properties, _properties);
588+
dataTracker.set(properties, List.copyOf(_properties));
590589
}
591590

592591
public List<AndroidScriptProperty> getProperties() {
@@ -595,7 +594,24 @@ public List<AndroidScriptProperty> getProperties() {
595594

596595
public void clearProperties() {
597596
_properties = new ArrayList<>();
598-
dataTracker.set(properties, _properties);
597+
dataTracker.set(properties, List.copyOf(_properties));
599598
}
600599

600+
public void applyProperty(AndroidScriptProperty prop) throws Exception {
601+
boolean found = false;
602+
for (var p : _properties) {
603+
if (Objects.equals(p.id, prop.id)) {
604+
p.value = prop.value;
605+
found = true;
606+
break;
607+
}
608+
}
609+
if(found) {
610+
dataTracker.set(properties, List.copyOf(_properties));
611+
System.out.printf("Applied variable %s (%s) = %s%n", prop.id, prop.displayName, prop.value);
612+
}
613+
else {
614+
throw new Exception("Can't apply prop because it was not found: " + prop.id + " (" + prop.displayName + ")");
615+
}
616+
}
601617
}

src/main/java/com/hanprogramer/androids/entities/android/AndroidScriptProperty.java

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package com.hanprogramer.androids.entities.android;
22

3+
import com.hanprogramer.androids.entities.android.scripting.BlockArea;
4+
import com.hanprogramer.androids.network.AndroidC2SSetPropertyPayload;
5+
import com.mojang.datafixers.util.Pair;
6+
import com.mojang.serialization.*;
7+
import net.minecraft.util.math.BlockPos;
38
import org.jetbrains.annotations.Nullable;
49

510
public class AndroidScriptProperty {
@@ -20,4 +25,90 @@ public AndroidScriptProperty(String name, String displayName, String type, @Null
2025
this.value = value;
2126
this.type = type;
2227
}
28+
29+
public static final Codec<AndroidScriptProperty> CODEC = createCodec();
30+
31+
private static Codec<AndroidScriptProperty> createCodec() {
32+
return new Codec<>() {
33+
34+
@Override
35+
public <T> DataResult<T> encode(AndroidScriptProperty prop, DynamicOps<T> ops, T prefix) {
36+
final RecordBuilder<T> builder = ops.mapBuilder();
37+
builder.add("id", ops.createString(prop.id));
38+
builder.add("displayName", ops.createString(prop.displayName));
39+
builder.add("type", ops.createString(prop.type));
40+
41+
if (prop.value != null) {
42+
return encodeValue(ops, prop.value, prop.type).flatMap(v -> {
43+
builder.add("value", v);
44+
return builder.build(prefix);
45+
});
46+
}
47+
return builder.build(prefix);
48+
}
49+
50+
@Override
51+
public <T> DataResult<Pair<AndroidScriptProperty, T>> decode(DynamicOps<T> ops, T input) {
52+
return ops.getMap(input).flatMap(map -> {
53+
DataResult<String> idDR = getRequiredString(ops, map, "id");
54+
DataResult<String> dnDR = getRequiredString(ops, map, "displayName");
55+
DataResult<String> typeDR = getRequiredString(ops, map, "type");
56+
57+
return idDR.flatMap(id ->
58+
dnDR.flatMap(display ->
59+
typeDR.flatMap(type -> {
60+
T valueT = map.get("value");
61+
return decodeValue(ops, valueT, type).map(val ->
62+
Pair.of(new AndroidScriptProperty(id, display, type, val), input)
63+
);
64+
})
65+
)
66+
);
67+
});
68+
}
69+
70+
private <T> DataResult<T> encodeValue(DynamicOps<T> ops, Object value, String type) {
71+
try {
72+
return switch (type) {
73+
case TYPE_BOOLEAN -> DataResult.success(ops.createBoolean((Boolean) value));
74+
case TYPE_INTEGER -> DataResult.success(ops.createInt(((Number) value).intValue()));
75+
case TYPE_FLOAT -> DataResult.success(ops.createFloat(((Number) value).floatValue()));
76+
case TYPE_STRING -> DataResult.success(ops.createString((String) value));
77+
case TYPE_BLOCKPOS -> BlockPos.CODEC.encodeStart(ops, (BlockPos) value);
78+
case TYPE_BLOCKAREA -> BlockArea.CODEC.encodeStart(ops, (BlockArea) value);
79+
default -> DataResult.error(() -> "Unknown type code: " + type);
80+
};
81+
} catch (ClassCastException ex) {
82+
return DataResult.error(() -> "Value does not match declared type '" + type + "': " + ex.getMessage());
83+
}
84+
}
85+
86+
private <T> DataResult<Object> decodeValue(DynamicOps<T> ops, @Nullable T valueElement, String type) {
87+
if (valueElement == null) {
88+
// value is nullable by class contract
89+
return DataResult.success(null);
90+
}
91+
return switch (type) {
92+
case TYPE_BOOLEAN -> ops.getBooleanValue(valueElement).map(b -> (Object) b);
93+
case TYPE_INTEGER -> ops.getNumberValue(valueElement).map(n -> (Object) n.intValue());
94+
case TYPE_FLOAT -> ops.getNumberValue(valueElement).map(n -> (Object) n.floatValue());
95+
case TYPE_STRING -> ops.getStringValue(valueElement).map(s -> (Object) s);
96+
case TYPE_BLOCKPOS -> BlockPos.CODEC.parse(ops, valueElement).map(p -> (Object) p);
97+
case TYPE_BLOCKAREA -> BlockArea.CODEC.parse(ops, valueElement).map(p -> (Object) p);
98+
default -> DataResult.error(() -> "Unknown type code: " + type);
99+
};
100+
}
101+
102+
private <T> DataResult<String> getRequiredString(DynamicOps<T> ops, MapLike<T> map, String key) {
103+
final T v = map.get(key);
104+
if (v == null) return DataResult.error(() -> "Missing field '" + key + "'");
105+
return ops.getStringValue(v);
106+
}
107+
};
108+
}
109+
110+
@Override
111+
public AndroidScriptProperty clone() {
112+
return new AndroidScriptProperty(id, displayName, type, value);
113+
}
23114
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,36 @@
11
package com.hanprogramer.androids.entities.android.scripting;
22

3+
import com.mojang.serialization.Codec;
4+
import com.mojang.serialization.codecs.RecordCodecBuilder;
35
import net.minecraft.util.math.BlockPos;
46

7+
import java.util.Objects;
8+
59
public class BlockArea {
610
public BlockPos pos1;
711
public BlockPos pos2;
812

13+
14+
public static final Codec<BlockArea> CODEC = RecordCodecBuilder.create(inst ->
15+
inst.group(
16+
BlockPos.CODEC.fieldOf("start").forGetter(b -> b.pos1),
17+
BlockPos.CODEC.fieldOf("end").forGetter(b -> b.pos2)
18+
).apply(inst, BlockArea::new)
19+
);
20+
921
public BlockArea(BlockPos pos1, BlockPos pos2) {
1022
this.pos1 = pos1;
1123
this.pos2 = pos2;
1224
}
25+
@Override
26+
public boolean equals(Object o) {
27+
if (this == o) return true;
28+
if (!(o instanceof BlockArea blockArea)) return false;
29+
return pos1.equals(blockArea.pos1) && pos2.equals(blockArea.pos2);
30+
}
31+
32+
@Override
33+
public int hashCode() {
34+
return Objects.hash(pos1, pos2);
35+
}
1336
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.hanprogramer.androids.network;
2+
3+
import com.hanprogramer.androids.AndroidsCraft;
4+
import com.hanprogramer.androids.entities.android.AndroidScriptProperty;
5+
import com.hanprogramer.androids.util.CodecPacketBridge;
6+
import net.minecraft.network.RegistryByteBuf;
7+
import net.minecraft.network.codec.PacketCodec;
8+
import net.minecraft.network.codec.PacketCodecs;
9+
import net.minecraft.network.packet.CustomPayload;
10+
import net.minecraft.util.Identifier;
11+
import net.minecraft.util.math.BlockPos;
12+
13+
public record AndroidC2SSetPropertyPayload(AndroidScriptProperty property, int entityId) implements CustomPayload {
14+
15+
public static final Identifier PAYLOAD_ID = Identifier.of(AndroidsCraft.MOD_ID, "android_set_property_c2s");
16+
17+
public static final Id<AndroidC2SSetPropertyPayload> ID = new Id<>(PAYLOAD_ID);
18+
public static final PacketCodec<RegistryByteBuf, AndroidScriptProperty> PROP_CODEC = CodecPacketBridge.ofCodec(AndroidScriptProperty.CODEC);
19+
public static final PacketCodec<RegistryByteBuf, AndroidC2SSetPropertyPayload> PACKET_CODEC = PacketCodec.of(
20+
(value, buf) -> {
21+
PacketCodecs.VAR_INT.encode(buf, value.entityId());
22+
PROP_CODEC.encode(buf, value.property());
23+
},
24+
buf -> {
25+
var entityId = PacketCodecs.VAR_INT.decode(buf);
26+
var prop = PROP_CODEC.decode(buf);
27+
return new AndroidC2SSetPropertyPayload(prop, entityId);
28+
}
29+
);
30+
31+
32+
@Override
33+
public Id<? extends CustomPayload> getId() {
34+
return ID;
35+
}
36+
}

0 commit comments

Comments
 (0)