Skip to content

Commit f87ce6a

Browse files
authored
Dynamic sync (#141)
* prototype * fix update listener * fix * some comments * Merge branch 'master' into dynamic-sync fix * remove some stuff * thing
1 parent e8b853d commit f87ce6a

27 files changed

+463
-57
lines changed

src/main/java/com/cleanroommc/modularui/CommonProxy.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
2222
import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
2323
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
24+
import net.minecraftforge.fml.common.gameevent.TickEvent;
2425
import net.minecraftforge.fml.common.registry.EntityEntry;
2526
import net.minecraftforge.fml.common.registry.EntityEntryBuilder;
2627
import net.minecraftforge.fml.relauncher.Side;
@@ -81,4 +82,11 @@ public void onConfigChange(ConfigChangedEvent.OnConfigChangedEvent event) {
8182
ConfigManager.sync(ModularUI.ID, Config.Type.INSTANCE);
8283
}
8384
}
85+
86+
@SubscribeEvent
87+
public static void onTick(TickEvent.PlayerTickEvent event) {
88+
if (event.player.openContainer instanceof ModularContainer container) {
89+
container.onUpdate();
90+
}
91+
}
8492
}

src/main/java/com/cleanroommc/modularui/api/IPacketWriter.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package com.cleanroommc.modularui.api;
22

3+
import io.netty.buffer.Unpooled;
4+
35
import net.minecraft.network.PacketBuffer;
46

57
import java.io.IOException;
68

79
/**
810
* A function that can write any data to an {@link PacketBuffer}.
911
*/
12+
@FunctionalInterface
1013
public interface IPacketWriter {
1114

1215
/**
@@ -16,4 +19,14 @@ public interface IPacketWriter {
1619
* @throws IOException if data can not be written for some reason
1720
*/
1821
void write(PacketBuffer buffer) throws IOException;
22+
23+
default PacketBuffer toPacket() {
24+
PacketBuffer buffer = new PacketBuffer(Unpooled.buffer());
25+
try {
26+
write(buffer);
27+
} catch (IOException e) {
28+
throw new RuntimeException(e);
29+
}
30+
return buffer;
31+
}
1932
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.cleanroommc.modularui.api;
2+
3+
import net.minecraft.network.PacketBuffer;
4+
5+
import org.jetbrains.annotations.ApiStatus;
6+
import org.jetbrains.annotations.NotNull;
7+
8+
@FunctionalInterface
9+
public interface ISyncedAction {
10+
11+
@ApiStatus.OverrideOnly
12+
void invoke(@NotNull PacketBuffer packet);
13+
}

src/main/java/com/cleanroommc/modularui/api/widget/ISynced.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ default W getThis() {
2424
* Called when this widget gets initialised or when this widget is added to the gui
2525
*
2626
* @param syncManager sync manager
27+
* @param late
2728
*/
28-
void initialiseSyncHandler(ModularSyncManager syncManager);
29+
void initialiseSyncHandler(ModularSyncManager syncManager, boolean late);
2930

3031
/**
3132
* Checks if the received sync handler is valid for this widget.

src/main/java/com/cleanroommc/modularui/api/widget/IWidget.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ public interface IWidget extends IGuiElement {
2727
* This element now becomes valid
2828
*
2929
* @param parent the parent this element belongs to
30+
* @param late true if this is called some time after the widget tree of the parent has been initialised
3031
*/
31-
void initialise(@NotNull IWidget parent);
32+
void initialise(@NotNull IWidget parent, boolean late);
3233

3334
/**
3435
* Invalidates this element.

src/main/java/com/cleanroommc/modularui/factory/GuiManager.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public static <T extends GuiData> void open(@NotNull UIFactory<T> factory, @NotN
7474
// create panel, collect sync handlers and create container
7575
UISettings settings = new UISettings(JeiSettings.DUMMY);
7676
settings.defaultCanInteractWith(factory, guiData);
77-
PanelSyncManager syncManager = new PanelSyncManager();
77+
PanelSyncManager syncManager = new PanelSyncManager(false);
7878
ModularPanel panel = factory.createPanel(guiData, syncManager, settings);
7979
WidgetTree.collectSyncValues(syncManager, panel);
8080
ModularContainer container = settings.hasContainer() ? settings.createContainer() : factory.createContainer();
@@ -100,7 +100,7 @@ public static <T extends GuiData> void openFromClient(int windowId, @NotNull UIF
100100
T guiData = factory.readGuiData(player, data);
101101
UISettings settings = new UISettings();
102102
settings.defaultCanInteractWith(factory, guiData);
103-
PanelSyncManager syncManager = new PanelSyncManager();
103+
PanelSyncManager syncManager = new PanelSyncManager(true);
104104
ModularPanel panel = factory.createPanel(guiData, syncManager, settings);
105105
WidgetTree.collectSyncValues(syncManager, panel);
106106
ModularScreen screen = factory.createScreen(guiData, panel);
Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package com.cleanroommc.modularui.network.packets;
22

3+
import com.cleanroommc.modularui.ModularUI;
34
import com.cleanroommc.modularui.network.IPacket;
45
import com.cleanroommc.modularui.network.NetworkUtils;
56
import com.cleanroommc.modularui.screen.ModularContainer;
67
import com.cleanroommc.modularui.screen.ModularScreen;
78

9+
import com.cleanroommc.modularui.value.sync.ModularSyncManager;
10+
811
import net.minecraft.client.network.NetHandlerPlayClient;
912
import net.minecraft.inventory.Container;
1013
import net.minecraft.network.NetHandlerPlayServer;
@@ -18,12 +21,12 @@ public class PacketSyncHandler implements IPacket {
1821

1922
private String panel;
2023
private String key;
24+
private boolean action;
2125
private PacketBuffer packet;
2226

23-
public PacketSyncHandler() {
24-
}
27+
public PacketSyncHandler() {}
2528

26-
public PacketSyncHandler(String panel, String key, PacketBuffer packet) {
29+
public PacketSyncHandler(String panel, String key, boolean action, PacketBuffer packet) {
2730
this.panel = panel;
2831
this.key = key;
2932
this.packet = packet;
@@ -33,25 +36,23 @@ public PacketSyncHandler(String panel, String key, PacketBuffer packet) {
3336
public void write(PacketBuffer buf) {
3437
NetworkUtils.writeStringSafe(buf, this.panel);
3538
NetworkUtils.writeStringSafe(buf, this.key, 64, true);
39+
buf.writeBoolean(this.action);
3640
NetworkUtils.writeByteBuf(buf, this.packet);
3741
}
3842

3943
@Override
4044
public void read(PacketBuffer buf) {
4145
this.panel = NetworkUtils.readStringSafe(buf);
4246
this.key = NetworkUtils.readStringSafe(buf);
47+
this.action = buf.readBoolean();
4348
this.packet = NetworkUtils.readPacketBuffer(buf);
4449
}
4550

4651
@Override
4752
public @Nullable IPacket executeClient(NetHandlerPlayClient handler) {
4853
ModularScreen screen = ModularScreen.getCurrent();
4954
if (screen != null) {
50-
try {
51-
screen.getSyncManager().receiveWidgetUpdate(this.panel, this.key, this.packet.readVarInt(), this.packet);
52-
} catch (IOException e) {
53-
throw new RuntimeException(e);
54-
}
55+
execute(screen.getSyncManager());
5556
}
5657
return null;
5758
}
@@ -60,12 +61,19 @@ public void read(PacketBuffer buf) {
6061
public @Nullable IPacket executeServer(NetHandlerPlayServer handler) {
6162
Container container = handler.player.openContainer;
6263
if (container instanceof ModularContainer modularContainer) {
63-
try {
64-
modularContainer.getSyncManager().receiveWidgetUpdate(this.panel, this.key, this.packet.readVarInt(), this.packet);
65-
} catch (IOException e) {
66-
throw new RuntimeException(e);
67-
}
64+
execute(modularContainer.getSyncManager());
6865
}
6966
return null;
7067
}
68+
69+
private void execute(ModularSyncManager syncManager) {
70+
try {
71+
int id = this.action ? 0 : this.packet.readVarInt();
72+
syncManager.receiveWidgetUpdate(this.panel, this.key, this.action, id, this.packet);
73+
} catch (IndexOutOfBoundsException e) {
74+
ModularUI.LOGGER.error("Failed to read packet for sync handler {} in panel {}", this.key, this.panel);
75+
} catch (IOException e) {
76+
ModularUI.LOGGER.throwing(e);
77+
}
78+
}
7179
}

src/main/java/com/cleanroommc/modularui/screen/ModularContainer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ public void detectAndSendChanges() {
112112
this.init = false;
113113
}
114114

115+
@ApiStatus.Internal
116+
public void onUpdate() {
117+
// detectAndSendChanges is potentially called multiple times per tick, while this method is called exactly once per tick
118+
if (this.syncManager != null) {
119+
this.syncManager.onUpdate();
120+
}
121+
}
122+
115123
private void sortShiftClickSlots() {
116124
this.shiftClickSlots.sort(Comparator.comparingInt(slot -> Objects.requireNonNull(slot.getSlotGroup()).getShiftClickPriority()));
117125
}

src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ public boolean canHover() {
219219
public void onOpen(ModularScreen screen) {
220220
this.screen = screen;
221221
getArea().z(1);
222-
initialise(this);
222+
initialise(this, false);
223223
// call first tick after everything is initialised
224224
WidgetTree.onUpdate(this);
225225
if (!isMainPanel() && shouldAnimate()) {

src/main/java/com/cleanroommc/modularui/test/TestTile.java

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.cleanroommc.modularui.drawable.Rectangle;
1010
import com.cleanroommc.modularui.drawable.text.AnimatedText;
1111
import com.cleanroommc.modularui.factory.PosGuiData;
12+
import com.cleanroommc.modularui.network.NetworkUtils;
1213
import com.cleanroommc.modularui.screen.ModularPanel;
1314
import com.cleanroommc.modularui.screen.RichTooltip;
1415
import com.cleanroommc.modularui.screen.UISettings;
@@ -20,10 +21,13 @@
2021
import com.cleanroommc.modularui.value.BoolValue;
2122
import com.cleanroommc.modularui.value.IntValue;
2223
import com.cleanroommc.modularui.value.StringValue;
24+
import com.cleanroommc.modularui.value.sync.DynamicSyncHandler;
2325
import com.cleanroommc.modularui.value.sync.GenericSyncValue;
2426
import com.cleanroommc.modularui.value.sync.IntSyncValue;
27+
import com.cleanroommc.modularui.value.sync.ItemSlotSH;
2528
import com.cleanroommc.modularui.value.sync.PanelSyncManager;
2629
import com.cleanroommc.modularui.value.sync.SyncHandlers;
30+
import com.cleanroommc.modularui.widget.EmptyWidget;
2731
import com.cleanroommc.modularui.widget.ParentWidget;
2832
import com.cleanroommc.modularui.widgets.*;
2933
import com.cleanroommc.modularui.widgets.layout.Column;
@@ -32,6 +36,10 @@
3236
import com.cleanroommc.modularui.widgets.slot.*;
3337
import com.cleanroommc.modularui.widgets.textfield.TextFieldWidget;
3438

39+
import it.unimi.dsi.fastutil.objects.Object2IntMap;
40+
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
41+
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
42+
3543
import net.minecraft.init.Blocks;
3644
import net.minecraft.init.Items;
3745
import net.minecraft.item.Item;
@@ -48,12 +56,22 @@
4856

4957
import java.util.Collection;
5058
import java.util.Random;
59+
import java.util.Map;
5160
import java.util.concurrent.atomic.AtomicInteger;
5261
import java.util.concurrent.atomic.AtomicReference;
5362
import java.util.function.Function;
5463

5564
public class TestTile extends TileEntity implements IGuiHolder<PosGuiData>, ITickable {
5665

66+
private static final Object2IntMap<Item> handlerSizeMap = new Object2IntOpenHashMap<>() {{
67+
put(Items.DIAMOND, 9);
68+
put(Items.EMERALD, 9);
69+
put(Items.GOLD_INGOT, 7);
70+
put(Items.IRON_INGOT, 6);
71+
put(Items.CLAY_BALL, 2);
72+
defaultReturnValue(3);
73+
}};
74+
5775
private final FluidTank fluidTank = new FluidTank(10000);
5876
private final FluidTank fluidTankPhantom = new FluidTank(Integer.MAX_VALUE);
5977
private long time = 0;
@@ -78,26 +96,44 @@ public int getSlotLimit(int slot) {
7896
private final FluidTank mixerFluids1 = new FluidTank(16000);
7997
private final FluidTank mixerFluids2 = new FluidTank(16000);
8098
private final ItemStackHandler craftingInventory = new ItemStackHandler(10);
99+
private final ItemStackHandler storageInventory0 = new ItemStackHandler(1);
100+
private final Map<Item, ItemStackHandler> stackHandlerMap = new Object2ObjectOpenHashMap<>();
81101

82102
private int num = 2;
83103

84104
@Override
85-
public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager, UISettings settings) {
105+
public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager syncManager, UISettings settings) {
86106
settings.customContainer(() -> new CraftingModularContainer(3, 3, this.craftingInventory));
87107

88-
guiSyncManager.registerSlotGroup("item_inv", 3);
89-
guiSyncManager.registerSlotGroup("mixer_items", 2);
108+
syncManager.registerSlotGroup("item_inv", 3);
109+
syncManager.registerSlotGroup("mixer_items", 2);
90110

91-
guiSyncManager.syncValue("mixer_fluids", 0, SyncHandlers.fluidSlot(this.mixerFluids1));
92-
guiSyncManager.syncValue("mixer_fluids", 1, SyncHandlers.fluidSlot(this.mixerFluids2));
111+
syncManager.syncValue("mixer_fluids", 0, SyncHandlers.fluidSlot(this.mixerFluids1));
112+
syncManager.syncValue("mixer_fluids", 1, SyncHandlers.fluidSlot(this.mixerFluids2));
93113
IntSyncValue cycleStateValue = new IntSyncValue(() -> this.cycleState, val -> this.cycleState = val);
94-
guiSyncManager.syncValue("cycle_state", cycleStateValue);
95-
guiSyncManager.syncValue("display_item", GenericSyncValue.forItem(() -> this.displayItem, null));
96-
guiSyncManager.bindPlayerInventory(guiData.getPlayer());
114+
syncManager.syncValue("cycle_state", cycleStateValue);
115+
syncManager.syncValue("display_item", GenericSyncValue.forItem(() -> this.displayItem, null));
116+
syncManager.bindPlayerInventory(guiData.getPlayer());
117+
118+
DynamicSyncHandler dynamicSyncHandler = new DynamicSyncHandler()
119+
.widgetProvider((syncManager1, packet) -> {
120+
ItemStack itemStack = NetworkUtils.readItemStack(packet);
121+
if (itemStack.isEmpty()) return new EmptyWidget();
122+
Item item = itemStack.getItem();
123+
ItemStackHandler handler = stackHandlerMap.computeIfAbsent(item, k -> new ItemStackHandler(handlerSizeMap.getInt(k)));
124+
String name = item.getRegistryName().toString();
125+
Flow flow = Flow.row();
126+
for (int i = 0; i < handler.getSlots(); i++) {
127+
int finalI = i;
128+
flow.child(new ItemSlot()
129+
.syncHandler(syncManager1.getOrCreateSyncHandler(name, i, ItemSlotSH.class, () -> new ItemSlotSH(new ModularSlot(handler, finalI)))));
130+
}
131+
return flow;
132+
});
97133

98134
Rectangle colorPickerBackground = new Rectangle().setColor(Color.RED.main);
99135
ModularPanel panel = new ModularPanel("test_tile");
100-
IPanelHandler panelSyncHandler = guiSyncManager.panel("other_panel", this::openSecondWindow, true);
136+
IPanelHandler panelSyncHandler = syncManager.panel("other_panel", this::openSecondWindow, true);
101137
IPanelHandler colorPicker = IPanelHandler.simple(panel, (mainPanel, player) -> new ColorPickerDialog(colorPickerBackground::setColor, colorPickerBackground.getColor(), true)
102138
.setDraggable(true)
103139
.relative(panel)
@@ -117,7 +153,10 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager,
117153
.child(new PageButton(1, tabController)
118154
.tab(GuiTextures.TAB_TOP, 0))
119155
.child(new PageButton(2, tabController)
120-
.tab(GuiTextures.TAB_TOP, 0)))
156+
.tab(GuiTextures.TAB_TOP, 0))
157+
.child(new PageButton(3, tabController)
158+
.tab(GuiTextures.TAB_TOP, 0)
159+
.overlay(new ItemDrawable(Blocks.CHEST).asIcon())))
121160
.child(new Expandable()
122161
.debugName("expandable")
123162
.top(0)
@@ -368,7 +407,22 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager,
368407
.size(14, 14))
369408
.child(IKey.lang("bogosort.gui.enabled").asWidget()
370409
.height(14)))))
371-
))
410+
.addPage(new ParentWidget<>()
411+
.debugName("page 4 storage")
412+
.sizeRel(1f)
413+
.child(new Column()
414+
.padding(7)
415+
.child(new ItemSlot()
416+
.slot(new ModularSlot(this.storageInventory0, 0)
417+
.changeListener(((newItem, onlyAmountChanged, client, init) -> {
418+
if (client && !onlyAmountChanged) {
419+
dynamicSyncHandler.notifyUpdate(packet -> NetworkUtils.writeItemStack(packet, newItem));
420+
}
421+
}))))
422+
.child(new DynamicSyncedWidget<>()
423+
.widthRel(1f)
424+
.syncHandler(dynamicSyncHandler)))
425+
)))
372426
.child(SlotGroupWidget.playerInventory(false))
373427
);
374428
/*panel.child(new ButtonWidget<>()

0 commit comments

Comments
 (0)