diff --git a/build.gradle b/build.gradle index 05b5e07..f549f34 100644 --- a/build.gradle +++ b/build.gradle @@ -44,7 +44,12 @@ allprojects { name = 'ParchmentMC' url = 'https://maven.parchmentmc.org' } + maven { + name = 'Rushmead' + url = 'https://mvn.imabad.dev/repository/maven-releases/' + } mavenCentral() + mavenLocal() // Add repositories to retrieve artifacts from in here. // You should only use this when depending on other mods because // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. diff --git a/common/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 b/common/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 index d76bb85..67b5aeb 100644 --- a/common/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 +++ b/common/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 @@ -1,2 +1,2 @@ -// 1.20.2 2023-12-11T00:30:48.3049893 Languages: en_us -dba74bb7077ce77df26626ba0432be702cf27329 assets/theatrical/lang/en_us.json +// 1.20.2 2024-05-27T10:52:44.9149037 Languages: en_us +008623f80efad0b7b210bfcdcd1374bc1dd74f63 assets/theatrical/lang/en_us.json diff --git a/common/src/generated/resources/assets/theatrical/lang/en_us.json b/common/src/generated/resources/assets/theatrical/lang/en_us.json index c8ff184..583d005 100644 --- a/common/src/generated/resources/assets/theatrical/lang/en_us.json +++ b/common/src/generated/resources/assets/theatrical/lang/en_us.json @@ -13,9 +13,11 @@ "block.theatrical.redstone_interface": "Redstone Interface", "block.theatrical.tank_trap": "Tank Trap", "block.theatrical.truss": "MT100 Truss", + "button.artnetconfig": "ArtNet Config", "fixture.dmxStart": "DMX Address", "fixture.pan": "Pan", "fixture.tilt": "Tilt", "itemGroup.theatrical": "Theatrical", + "screen.artnetconfig.enabled": "ArtNet Enabled: %s", "screen.movinglight": "Moving Light" } \ No newline at end of file diff --git a/common/src/main/java/dev/imabad/theatrical/Constants.java b/common/src/main/java/dev/imabad/theatrical/Constants.java new file mode 100644 index 0000000..36dda57 --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/Constants.java @@ -0,0 +1,5 @@ +package dev.imabad.theatrical; + +public class Constants { + public static final short MANUFACTURER_ID = 0x7FF0; +} diff --git a/common/src/main/java/dev/imabad/theatrical/Theatrical.java b/common/src/main/java/dev/imabad/theatrical/Theatrical.java index 40a30ea..133c1f8 100644 --- a/common/src/main/java/dev/imabad/theatrical/Theatrical.java +++ b/common/src/main/java/dev/imabad/theatrical/Theatrical.java @@ -1,5 +1,6 @@ package dev.imabad.theatrical; +import dev.architectury.event.events.common.PlayerEvent; import dev.architectury.platform.Platform; import dev.architectury.registry.CreativeTabRegistry; import dev.architectury.registry.registries.DeferredRegister; @@ -8,8 +9,11 @@ import dev.imabad.theatrical.blocks.Blocks; import dev.imabad.theatrical.config.ConfigHandler; import dev.imabad.theatrical.config.TheatricalConfig; +import dev.imabad.theatrical.dmx.DMXDevice; +import dev.imabad.theatrical.dmx.DMXNetworkData; import dev.imabad.theatrical.fixtures.Fixtures; import dev.imabad.theatrical.items.Items; +import dev.imabad.theatrical.net.artnet.ListConsumers; import dev.imabad.theatrical.net.TheatricalNet; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.Component; @@ -18,6 +22,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.List; + public class Theatrical { public static final String MOD_ID = "theatrical"; // Registering a new creative tab @@ -34,7 +41,7 @@ public class Theatrical { public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); public static void init() { - ConfigHandler configHandler = new ConfigHandler(Platform.getConfigFolder()); + ConfigHandler configHandler = ConfigHandler.initialize(Platform.getConfigFolder()); TheatricalConfig.INSTANCE.register(configHandler); TABS.register(); Fixtures.init(); @@ -42,7 +49,22 @@ public static void init() { Blocks.BLOCKS.register(); BlockEntities.BLOCK_ENTITIES.register(); dev.imabad.theatrical.items.Items.ITEMS.register(); + PlayerEvent.PLAYER_JOIN.register((event) -> { + if(event.connection.player.hasPermissions(event.getServer().getOperatorUserPermissionLevel())){ + DMXNetworkData.getInstance().addKnownSender(event.connection.player); + } + for (Integer universe : DMXNetworkData.getInstance().getUniverses()) { + List devices = new ArrayList<>(); + DMXNetworkData.getInstance().getConsumers(universe).forEach(consumer -> { + devices.add(new DMXDevice(consumer.getDeviceId(), consumer.getChannelStart(), + consumer.getChannelCount(), consumer.getDeviceTypeId(), consumer.getActivePersonality(), consumer.getModelName(), + consumer.getFixtureId())); + }); + new ListConsumers(universe, devices).sendTo(event.connection.player); + } + }); + PlayerEvent.PLAYER_QUIT.register((event) -> { + DMXNetworkData.getInstance().removeKnownSender(event.connection.player); + }); } - - } diff --git a/common/src/main/java/dev/imabad/theatrical/TheatricalClient.java b/common/src/main/java/dev/imabad/theatrical/TheatricalClient.java index 7be6de5..88f3ee2 100644 --- a/common/src/main/java/dev/imabad/theatrical/TheatricalClient.java +++ b/common/src/main/java/dev/imabad/theatrical/TheatricalClient.java @@ -3,6 +3,7 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Axis; +import dev.architectury.event.events.client.ClientPlayerEvent; import dev.architectury.registry.client.rendering.BlockEntityRendererRegistry; import dev.imabad.theatrical.blockentities.BlockEntities; import dev.imabad.theatrical.blockentities.light.BaseLightBlockEntity; @@ -10,8 +11,12 @@ import dev.imabad.theatrical.client.blockentities.FresnelRenderer; import dev.imabad.theatrical.client.blockentities.LEDPanelRenderer; import dev.imabad.theatrical.client.blockentities.MovingLightRenderer; -import dev.imabad.theatrical.fixtures.Fixtures; +import dev.imabad.theatrical.config.TheatricalConfig; +import dev.imabad.theatrical.dmx.DMXDevice; +import dev.imabad.theatrical.dmx.TheatricalArtNetClient; import dev.imabad.theatrical.lighting.LightManager; +import dev.imabad.theatrical.net.artnet.ListConsumers; +import dev.imabad.theatrical.net.artnet.NotifyConsumerChange; import dev.imabad.theatrical.protocols.artnet.ArtNetManager; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; @@ -43,6 +48,14 @@ public static void init() { BlockEntityRendererRegistry.register(BlockEntities.LED_PANEL.get(), LEDPanelRenderer::new); // BlockEntityRendererRegistry.register(BlockEntities.CABLE.get(), CableRenderer::new); artNetManager = new ArtNetManager(); + ClientPlayerEvent.CLIENT_PLAYER_JOIN.register((event) -> { + if(TheatricalConfig.INSTANCE.CLIENT.artnetEnabled){ + artNetManager.getClient(); + } + }); + ClientPlayerEvent.CLIENT_PLAYER_QUIT.register((event) -> { + onWorldClose(); + }); } public static ArtNetManager getArtNetManager(){ @@ -61,6 +74,10 @@ public static float[] renderThings(BlockPos MY_BLOCK, VertexConsumer consumer, P return new float[]{be.getTilt(), be.getPan()}; } + public static void onWorldClose(){ + artNetManager.shutdownAll(); + } + public static void renderWorldLastAfterTripwire(LevelRenderer levelRenderer){ LightManager.updateAll(levelRenderer); } @@ -128,4 +145,31 @@ public static byte[] UUID2Bytes(UUID uuid) { long lo = uuid.getLeastSignificantBits(); return ByteBuffer.allocate(16).putLong(hi).putLong(lo).array(); } + + public static void handleConsumerChange(NotifyConsumerChange notifyConsumerChange){ + if(TheatricalConfig.INSTANCE.CLIENT.artnetEnabled){ + TheatricalArtNetClient artNetClient = getArtNetManager().getClient(); + if(artNetClient.isSubscribedTo(notifyConsumerChange.getUniverse())){ + DMXDevice dmxDevice = notifyConsumerChange.getDmxDevice(); + if(notifyConsumerChange.getChangeType() == NotifyConsumerChange.ChangeType.ADD){ + artNetClient.addDevice(notifyConsumerChange.getUniverse(), dmxDevice.getDeviceId(), dmxDevice); + } else if(notifyConsumerChange.getChangeType() == NotifyConsumerChange.ChangeType.UPDATE) { + artNetClient.updateDevice(notifyConsumerChange.getUniverse(), dmxDevice.getDeviceId(), dmxDevice); + } else { + artNetClient.removeDevice(notifyConsumerChange.getUniverse(), dmxDevice.getDeviceId()); + } + } + } + } + + public static void handleListConsumers(ListConsumers listConsumers){ + if(TheatricalConfig.INSTANCE.CLIENT.artnetEnabled) { + TheatricalArtNetClient artNetClient = getArtNetManager().getClient(); + if (artNetClient.isSubscribedTo(listConsumers.getUniverse())) { + for (DMXDevice dmxDevice : listConsumers.getDmxDevices()) { + artNetClient.addDevice(listConsumers.getUniverse(), dmxDevice.getDeviceId(), dmxDevice); + } + } + } + } } diff --git a/common/src/main/java/dev/imabad/theatrical/TheatricalExpectPlatform.java b/common/src/main/java/dev/imabad/theatrical/TheatricalExpectPlatform.java index 5780639..f3166b2 100644 --- a/common/src/main/java/dev/imabad/theatrical/TheatricalExpectPlatform.java +++ b/common/src/main/java/dev/imabad/theatrical/TheatricalExpectPlatform.java @@ -19,5 +19,9 @@ public static Path getConfigDirectory() { public static BakedModel getBakedModel(ResourceLocation modelLocation){ throw new AssertionError(); } + @ExpectPlatform + public static String getModVersion() { + throw new AssertionError(); + } } diff --git a/common/src/main/java/dev/imabad/theatrical/api/Fixture.java b/common/src/main/java/dev/imabad/theatrical/api/Fixture.java index 6c3eb1f..de4c0e7 100644 --- a/common/src/main/java/dev/imabad/theatrical/api/Fixture.java +++ b/common/src/main/java/dev/imabad/theatrical/api/Fixture.java @@ -1,10 +1,13 @@ package dev.imabad.theatrical.api; +import dev.imabad.theatrical.api.dmx.DMXPersonality; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.state.BlockState; +import java.util.List; + public abstract class Fixture { public abstract ResourceLocation getTiltModel(); @@ -36,4 +39,5 @@ public boolean hasBeam(){ return true; } public abstract float[] getTransforms(BlockState fixtureBlockState, BlockState supportBlockState); + public abstract List getDMXPersonalities(); } diff --git a/common/src/main/java/dev/imabad/theatrical/api/dmx/DMXConsumer.java b/common/src/main/java/dev/imabad/theatrical/api/dmx/DMXConsumer.java index 458ef48..cdf3bbc 100644 --- a/common/src/main/java/dev/imabad/theatrical/api/dmx/DMXConsumer.java +++ b/common/src/main/java/dev/imabad/theatrical/api/dmx/DMXConsumer.java @@ -1,11 +1,26 @@ package dev.imabad.theatrical.api.dmx; +import ch.bildspur.artnet.rdm.RDMDeviceId; +import net.minecraft.resources.ResourceLocation; + public interface DMXConsumer { int getChannelCount(); int getChannelStart(); + int getUniverse(); + void consume(byte[] dmxValues); + RDMDeviceId getDeviceId(); + + int getDeviceTypeId(); + + String getModelName(); + + ResourceLocation getFixtureId(); + + int getActivePersonality(); + } diff --git a/common/src/main/java/dev/imabad/theatrical/api/dmx/DMXPersonality.java b/common/src/main/java/dev/imabad/theatrical/api/dmx/DMXPersonality.java new file mode 100644 index 0000000..b185cc5 --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/api/dmx/DMXPersonality.java @@ -0,0 +1,38 @@ +package dev.imabad.theatrical.api.dmx; + +import java.util.ArrayList; +import java.util.List; + +public class DMXPersonality { + private final int channelCount; + private final String description; + private final List slots; + + public DMXPersonality(int channelCount, String description){ + this.channelCount = channelCount; + this.description = description; + this.slots = new ArrayList<>(); + } + + public DMXPersonality addSlot(DMXSlot slot){ + slots.add(slot); + return this; + } + + public DMXPersonality addSlot(int position, DMXSlot slot){ + slots.add(position, slot); + return this; + } + + public int getChannelCount() { + return channelCount; + } + + public String getDescription() { + return description; + } + + public List getSlots() { + return slots; + } +} diff --git a/common/src/main/java/dev/imabad/theatrical/api/dmx/DMXSlot.java b/common/src/main/java/dev/imabad/theatrical/api/dmx/DMXSlot.java new file mode 100644 index 0000000..78c9189 --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/api/dmx/DMXSlot.java @@ -0,0 +1,7 @@ +package dev.imabad.theatrical.api.dmx; + +import ch.bildspur.artnet.rdm.RDMSlotID; +import ch.bildspur.artnet.rdm.RDMSlotType; + +public record DMXSlot(String label, RDMSlotType slotType, RDMSlotID slotID) { +} diff --git a/common/src/main/java/dev/imabad/theatrical/blockentities/interfaces/ArtNetInterfaceBlockEntity.java b/common/src/main/java/dev/imabad/theatrical/blockentities/interfaces/ArtNetInterfaceBlockEntity.java index 5a6cf0c..6832ead 100644 --- a/common/src/main/java/dev/imabad/theatrical/blockentities/interfaces/ArtNetInterfaceBlockEntity.java +++ b/common/src/main/java/dev/imabad/theatrical/blockentities/interfaces/ArtNetInterfaceBlockEntity.java @@ -1,11 +1,9 @@ package dev.imabad.theatrical.blockentities.interfaces; -import dev.imabad.theatrical.TheatricalClient; import dev.imabad.theatrical.blockentities.BlockEntities; import dev.imabad.theatrical.blockentities.ClientSyncBlockEntity; import dev.imabad.theatrical.config.TheatricalConfig; import dev.imabad.theatrical.dmx.DMXNetworkData; -import dev.imabad.theatrical.net.SendArtNetData; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; @@ -19,12 +17,16 @@ public class ArtNetInterfaceBlockEntity extends ClientSyncBlockEntity { public static void tick(Level level, BlockPos pos, BlockState state, T be) { ArtNetInterfaceBlockEntity tile = (ArtNetInterfaceBlockEntity) be; - if(level.isClientSide){ - if(tile.isOwnedByCurrentClient()){ - byte[] data = TheatricalClient.getArtNetManager().getClient(tile.ip).readDmxData(tile.subnet, tile.universe); - new SendArtNetData(pos, data).sendToServer(); - } - } +// if(level.isClientSide){ +// if(tile.isOwnedByCurrentClient()){ +// TheatricalArtNetClient client = TheatricalClient.getArtNetManager().getClient(tile.ip); +// if(!client.isSubscribedTo(tile.universe)){ +// client.subscribeToUniverse(tile.universe); +// } +// byte[] data = client.readDmxData(tile.subnet, tile.universe); +// new SendArtNetData(pos, data).sendToServer(); +// } +// } } private int subnet, universe, tickTimer = 0; @@ -40,7 +42,9 @@ public void write(CompoundTag compoundTag) { compoundTag.putString("ip", ip); compoundTag.putInt("subnet", subnet); compoundTag.putInt("universe", universe); - compoundTag.putUUID("ownerUUID", ownerUUID); + if(ownerUUID != null) { + compoundTag.putUUID("ownerUUID", ownerUUID); + } } @Override @@ -48,14 +52,16 @@ public void read(CompoundTag compoundTag) { this.ip = compoundTag.getString("ip"); this.subnet = compoundTag.getInt("subnet"); this.universe = compoundTag.getInt("universe"); - this.ownerUUID = compoundTag.getUUID("ownerUUID"); + if(compoundTag.contains("ownerUUID")) { + this.ownerUUID = compoundTag.getUUID("ownerUUID"); + } } public void update(byte[] data) { if(level != null && level.getServer() != null) { var dmxData = DMXNetworkData.getInstance(); if(dmxData != null) { - dmxData.getConsumersInRange(getBlockPos(), TheatricalConfig.INSTANCE.COMMON.wirelessDMXRadius).forEach(dmxConsumer -> dmxConsumer.consume(data)); + dmxData.getConsumersInRange(universe, getBlockPos(), TheatricalConfig.INSTANCE.COMMON.wirelessDMXRadius).forEach(dmxConsumer -> dmxConsumer.consume(data)); } } } @@ -71,16 +77,16 @@ public boolean isOwnedByCurrentClient(){ } public boolean hasReceivedPacket(){ - if(level != null && level.isClientSide){ - return TheatricalClient.getArtNetManager().getClient(this.ip).hasReceivedPacket(); - } +// if(level != null && level.isClientSide){ +// return TheatricalClient.getArtNetManager().getClient(this.ip).hasReceivedPacket(); +// } return false; } public long getLastReceivedPacket(){ - if(level != null && level.isClientSide){ - return TheatricalClient.getArtNetManager().getClient(this.ip).getLastPacketMS(); - } +// if(level != null && level.isClientSide){ +// return TheatricalClient.getArtNetManager().getClient(this.ip).getLastPacketMS(); +// } return 0; } diff --git a/common/src/main/java/dev/imabad/theatrical/blockentities/interfaces/RedstoneInterfaceBlockEntity.java b/common/src/main/java/dev/imabad/theatrical/blockentities/interfaces/RedstoneInterfaceBlockEntity.java index 0f654c9..e385a57 100644 --- a/common/src/main/java/dev/imabad/theatrical/blockentities/interfaces/RedstoneInterfaceBlockEntity.java +++ b/common/src/main/java/dev/imabad/theatrical/blockentities/interfaces/RedstoneInterfaceBlockEntity.java @@ -1,11 +1,16 @@ package dev.imabad.theatrical.blockentities.interfaces; +import ch.bildspur.artnet.rdm.RDMDeviceId; +import dev.imabad.theatrical.Constants; import dev.imabad.theatrical.api.dmx.DMXConsumer; import dev.imabad.theatrical.blockentities.BlockEntities; import dev.imabad.theatrical.blockentities.ClientSyncBlockEntity; import dev.imabad.theatrical.dmx.DMXNetworkData; +import dev.imabad.theatrical.fixtures.Fixtures; +import dev.imabad.theatrical.util.RndUtils; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; @@ -13,11 +18,13 @@ import net.minecraft.world.level.block.state.BlockState; import java.util.Arrays; +import java.util.Random; public class RedstoneInterfaceBlockEntity extends ClientSyncBlockEntity implements DMXConsumer { - private int channelStartPoint = 0; + private int channelStartPoint, dmxUniverse = 0; private int redstoneOutput = 0; + private RDMDeviceId deviceId; public RedstoneInterfaceBlockEntity(BlockPos blockPos, BlockState blockState) { super(BlockEntities.REDSTONE_INTERFACE.get(), blockPos, blockState); @@ -27,11 +34,19 @@ public RedstoneInterfaceBlockEntity(BlockPos blockPos, BlockState blockState) { public void write(CompoundTag compoundTag) { compoundTag.putInt("channelCount", 1); compoundTag.putInt("channelStartPoint", channelStartPoint); + compoundTag.putInt("dmxUniverse", dmxUniverse); + compoundTag.putByteArray("deviceId", deviceId.toBytes()); } @Override public void read(CompoundTag compoundTag) { channelStartPoint = compoundTag.getInt("channelStartPoint"); + if(compoundTag.contains("dmxUniverse")){ + dmxUniverse = compoundTag.getInt("dmxUniverse"); + } + if(compoundTag.contains("deviceId")){ + deviceId = new RDMDeviceId(compoundTag.getByteArray("deviceId")); + } } @Override @@ -44,10 +59,51 @@ public int getChannelStart() { return channelStartPoint; } + @Override + public int getUniverse() { + return dmxUniverse; + } + public int getRedstoneOutput() { return redstoneOutput; } + @Override + public RDMDeviceId getDeviceId() { + return deviceId; + } + + @Override + public int getDeviceTypeId() { + return 0x04; + } + + @Override + public String getModelName() { + return "Redstone Interface"; + } + + @Override + public ResourceLocation getFixtureId() { + return Fixtures.REDSTONE_INTERFACE.getId(); + } + + @Override + public int getActivePersonality() { + return 0; + } + + private void generateDeviceId(){ + byte[] bytes = new byte[4]; + if(level != null) { + RndUtils.nextBytes(level.getRandom(), bytes); + } else { + new Random().nextBytes(bytes); + } + deviceId = new RDMDeviceId(Constants.MANUFACTURER_ID, bytes); + level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_CLIENTS); + } + @Override public void consume(byte[] dmxValues) { byte[] ourValues = Arrays.copyOfRange(dmxValues, this.getChannelStart(), @@ -67,27 +123,56 @@ public int convertByteToInt(byte val) { public void setChannelStartPoint(int channelStartPoint) { this.channelStartPoint = channelStartPoint; + updateConsumer(); + level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_CLIENTS); + } + + public void setUniverse(int universe) { + if(this.dmxUniverse == universe){ + return; + } + removeConsumer(); + this.dmxUniverse = universe; + addConsumer(); level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_CLIENTS); } + private void updateConsumer(){ + var dmxData = DMXNetworkData.getInstance(); + if (dmxData != null) { + dmxData.updateConsumer(this); + } + } + + private void addConsumer(){ + var dmxData = DMXNetworkData.getInstance(); + if (dmxData != null) { + if(deviceId == null){ + generateDeviceId(); + } + dmxData.addConsumer(getBlockPos(), this); + } + } + + private void removeConsumer(){ + var dmxData = DMXNetworkData.getInstance(); + if (dmxData != null) { + dmxData.removeConsumer(this, getBlockPos()); + } + } + @Override public void setLevel(Level level) { super.setLevel(level); if(level != null && !level.isClientSide) { - var dmxData = DMXNetworkData.getInstance(); - if (dmxData != null) { - dmxData.addConsumer(getBlockPos(), this); - } + addConsumer(); } } @Override public void setRemoved() { if(level != null && !level.isClientSide) { - var dmxData = DMXNetworkData.getInstance(); - if (dmxData != null) { - dmxData.removeConsumer(getBlockPos()); - } + removeConsumer(); } super.setRemoved(); } diff --git a/common/src/main/java/dev/imabad/theatrical/blockentities/light/BaseDMXConsumerLightBlockEntity.java b/common/src/main/java/dev/imabad/theatrical/blockentities/light/BaseDMXConsumerLightBlockEntity.java index 7d329ae..529dc9d 100644 --- a/common/src/main/java/dev/imabad/theatrical/blockentities/light/BaseDMXConsumerLightBlockEntity.java +++ b/common/src/main/java/dev/imabad/theatrical/blockentities/light/BaseDMXConsumerLightBlockEntity.java @@ -1,7 +1,10 @@ package dev.imabad.theatrical.blockentities.light; +import ch.bildspur.artnet.rdm.RDMDeviceId; +import dev.imabad.theatrical.Constants; import dev.imabad.theatrical.api.dmx.DMXConsumer; import dev.imabad.theatrical.dmx.DMXNetworkData; +import dev.imabad.theatrical.util.RndUtils; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.level.Level; @@ -9,9 +12,13 @@ import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import java.util.Arrays; +import java.util.Random; + public abstract class BaseDMXConsumerLightBlockEntity extends BaseLightBlockEntity implements DMXConsumer { - private int channelCount, channelStartPoint; + private int channelCount, channelStartPoint, dmxUniverse; + private RDMDeviceId deviceId; public BaseDMXConsumerLightBlockEntity(BlockEntityType blockEntityType, BlockPos blockPos, BlockState blockState) { super(blockEntityType, blockPos, blockState); @@ -22,6 +29,8 @@ public void write(CompoundTag compoundTag) { super.write(compoundTag); compoundTag.putInt("channelCount", channelCount); compoundTag.putInt("channelStartPoint", channelStartPoint); + compoundTag.putInt("dmxUniverse", dmxUniverse); + compoundTag.putByteArray("deviceId", deviceId.toBytes()); } @Override @@ -29,6 +38,23 @@ public void read(CompoundTag compoundTag) { super.read(compoundTag); channelCount = compoundTag.getInt("channelCount"); channelStartPoint = compoundTag.getInt("channelStartPoint"); + if(compoundTag.contains("dmxUniverse")){ + dmxUniverse = compoundTag.getInt("dmxUniverse"); + } + if(compoundTag.contains("deviceId")){ + deviceId = new RDMDeviceId(compoundTag.getByteArray("deviceId")); + } + } + + private void generateDeviceId(){ + byte[] bytes = new byte[4]; + if(level != null) { + RndUtils.nextBytes(level.getRandom(), bytes); + } else { + new Random().nextBytes(bytes); + } + deviceId = new RDMDeviceId(Constants.MANUFACTURER_ID, bytes); + level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_CLIENTS); } @Override @@ -41,33 +67,72 @@ public int getChannelStart() { return channelStartPoint; } + @Override + public int getUniverse() { + return dmxUniverse; + } + + @Override + public RDMDeviceId getDeviceId() { + return deviceId; + } + + public void setUniverse(int dmxUniverse) { + if(this.dmxUniverse == dmxUniverse){ + return; + } + removeConsumer(); + this.dmxUniverse = dmxUniverse; + addConsumer(); + level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_CLIENTS); + } + public void setChannelCount(int channelCount) { this.channelCount = channelCount; } public void setChannelStartPoint(int channelStartPoint) { + if(this.channelStartPoint == channelStartPoint){ + return; + } this.channelStartPoint = channelStartPoint; + updateConsumer(); level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_CLIENTS); } + private void updateConsumer(){ + var dmxData = DMXNetworkData.getInstance(); + if (dmxData != null) { + dmxData.updateConsumer(this); + } + } + private void removeConsumer(){ + var dmxData = DMXNetworkData.getInstance(); + if (dmxData != null) { + dmxData.removeConsumer(this, getBlockPos()); + } + } + private void addConsumer(){ + var dmxData = DMXNetworkData.getInstance(); + if (dmxData != null) { + if(deviceId == null){ + generateDeviceId(); + } + dmxData.addConsumer(getBlockPos(), this); + } + } @Override public void setLevel(Level level) { super.setLevel(level); if(level != null && !level.isClientSide) { - var dmxData = DMXNetworkData.getInstance(); - if (dmxData != null) { - dmxData.addConsumer(getBlockPos(), this); - } + addConsumer(); } } @Override public void setRemoved() { if(level != null && !level.isClientSide) { - var dmxData = DMXNetworkData.getInstance(); - if (dmxData != null) { - dmxData.removeConsumer(getBlockPos()); - } + removeConsumer(); } super.setRemoved(); } diff --git a/common/src/main/java/dev/imabad/theatrical/blockentities/light/FresnelBlockEntity.java b/common/src/main/java/dev/imabad/theatrical/blockentities/light/FresnelBlockEntity.java index a42b2bf..d3ef39f 100644 --- a/common/src/main/java/dev/imabad/theatrical/blockentities/light/FresnelBlockEntity.java +++ b/common/src/main/java/dev/imabad/theatrical/blockentities/light/FresnelBlockEntity.java @@ -5,6 +5,7 @@ import dev.imabad.theatrical.blocks.light.MovingLightBlock; import dev.imabad.theatrical.fixtures.Fixtures; import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -43,6 +44,27 @@ public void consume(byte[] dmxValues) { blue = convertByteToInt(ourValues[3]); level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_CLIENTS); } + + @Override + public int getDeviceTypeId() { + return 0x02; + } + + @Override + public String getModelName() { + return "LED Fresnel"; + } + + @Override + public ResourceLocation getFixtureId() { + return Fixtures.LED_FRESNEL.getId(); + } + + @Override + public int getActivePersonality() { + return 0; + } + public int convertByteToInt(byte val) { return Byte.toUnsignedInt(val); } diff --git a/common/src/main/java/dev/imabad/theatrical/blockentities/light/LEDPanelBlockEntity.java b/common/src/main/java/dev/imabad/theatrical/blockentities/light/LEDPanelBlockEntity.java index ce4d3a5..9f6c083 100644 --- a/common/src/main/java/dev/imabad/theatrical/blockentities/light/LEDPanelBlockEntity.java +++ b/common/src/main/java/dev/imabad/theatrical/blockentities/light/LEDPanelBlockEntity.java @@ -4,6 +4,7 @@ import dev.imabad.theatrical.blockentities.BlockEntities; import dev.imabad.theatrical.fixtures.Fixtures; import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -37,6 +38,27 @@ public void consume(byte[] dmxValues) { blue = convertByteToInt(ourValues[3]); level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_CLIENTS); } + + @Override + public int getDeviceTypeId() { + return 0x03; + } + + @Override + public String getModelName() { + return "LED Panel"; + } + + @Override + public ResourceLocation getFixtureId() { + return Fixtures.LED_PANEL.getId(); + } + + @Override + public int getActivePersonality() { + return 0; + } + public int convertByteToInt(byte val) { return Byte.toUnsignedInt(val); } diff --git a/common/src/main/java/dev/imabad/theatrical/blockentities/light/MovingLightBlockEntity.java b/common/src/main/java/dev/imabad/theatrical/blockentities/light/MovingLightBlockEntity.java index 0b483dd..3c44330 100644 --- a/common/src/main/java/dev/imabad/theatrical/blockentities/light/MovingLightBlockEntity.java +++ b/common/src/main/java/dev/imabad/theatrical/blockentities/light/MovingLightBlockEntity.java @@ -6,6 +6,7 @@ import dev.imabad.theatrical.fixtures.Fixtures; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -28,8 +29,9 @@ public Fixture getFixture() { @Override public void consume(byte[] dmxValues) { - byte[] ourValues = Arrays.copyOfRange(dmxValues, this.getChannelStart(), - this.getChannelStart() + this.getChannelCount()); + int start = this.getChannelStart() > 0 ? this.getChannelStart() - 1 : 0; + byte[] ourValues = Arrays.copyOfRange(dmxValues, start, + start+ this.getChannelCount()); if(ourValues.length < 7){ return; } @@ -45,6 +47,27 @@ public void consume(byte[] dmxValues) { tilt = (int) ((convertByteToInt(ourValues[6]) * 180) / 255F) - 180; level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_CLIENTS); } + + @Override + public int getDeviceTypeId() { + return 0x01; + } + + @Override + public String getModelName() { + return "Moving Head"; + } + + @Override + public ResourceLocation getFixtureId() { + return Fixtures.MOVING_LIGHT.getId(); + } + + @Override + public int getActivePersonality() { + return 0; + } + public int convertByteToInt(byte val) { return Byte.toUnsignedInt(val); } diff --git a/common/src/main/java/dev/imabad/theatrical/client/gui/screen/ArtNetConfigurationScreen.java b/common/src/main/java/dev/imabad/theatrical/client/gui/screen/ArtNetConfigurationScreen.java new file mode 100644 index 0000000..de37f59 --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/client/gui/screen/ArtNetConfigurationScreen.java @@ -0,0 +1,233 @@ +package dev.imabad.theatrical.client.gui.screen; + +import com.mojang.serialization.Codec; +import dev.imabad.theatrical.Theatrical; +import dev.imabad.theatrical.TheatricalClient; +import dev.imabad.theatrical.client.gui.widgets.LabeledEditBox; +import dev.imabad.theatrical.config.ConfigHandler; +import dev.imabad.theatrical.config.TheatricalConfig; +import dev.imabad.theatrical.net.UpdateArtNetInterface; +import net.minecraft.client.Minecraft; +import net.minecraft.client.OptionInstance; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.CycleButton; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.layouts.FrameLayout; +import net.minecraft.client.gui.layouts.LinearLayout; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class ArtNetConfigurationScreen extends Screen { + + private int xCenter, yCenter; + private EditBox universeBox1,universeBox2,universeBox3,universeBox4, ipAddressBox; + private int[] universe; + private String ipAddress; + private boolean enabled; + private Screen lastScreen; + private LinearLayout layout; + + public ArtNetConfigurationScreen(Screen lastScreen) { + super(Component.translatable("button.artnetconfig")); + universe = new int[4]; + universe[0] = TheatricalConfig.INSTANCE.CLIENT.universe1; + universe[1] = TheatricalConfig.INSTANCE.CLIENT.universe2; + universe[2] = TheatricalConfig.INSTANCE.CLIENT.universe3; + universe[3] = TheatricalConfig.INSTANCE.CLIENT.universe4; + this.ipAddress = TheatricalConfig.INSTANCE.CLIENT.artNetIP; + this.enabled = TheatricalConfig.INSTANCE.CLIENT.artnetEnabled; + this.lastScreen = lastScreen; + } + + @Override + protected void init() { + super.init(); + layout = LinearLayout.vertical(); + layout.defaultCellSetting().alignHorizontallyCenter().padding(10); + xCenter = (this.width / 2); + yCenter = (this.height / 2); + this.ipAddressBox = new LabeledEditBox(this.font, xCenter, yCenter, 100, 20, Component.translatable("artneti.ipAddress")); + this.ipAddressBox.setValue(ipAddress); + layout.addChild(this.ipAddressBox); + this.universeBox1 = new LabeledEditBox(this.font, xCenter, yCenter, 100, 20, Component.translatable("artneti.dmxUniverse").append(" 1")); + this.universeBox1.setValue(Integer.toString(universe[0])); + layout.addChild(this.universeBox1); + this.universeBox2 = new LabeledEditBox(this.font, xCenter, yCenter , 100, 20, (Component)Component.translatable("artneti.dmxUniverse").append(" 2")); + this.universeBox2.setValue(Integer.toString(universe[1])); + layout.addChild(this.universeBox2); + this.universeBox3 = new LabeledEditBox(this.font, xCenter, yCenter , 100, 20, (Component)Component.translatable("artneti.dmxUniverse").append(" 3")); + this.universeBox3.setValue(Integer.toString(universe[2])); + layout.addChild(this.universeBox3); + this.universeBox4 = new LabeledEditBox(this.font, xCenter, yCenter, 100, 20, (Component)Component.translatable("artneti.dmxUniverse").append(" 4")); + this.universeBox4.setValue(Integer.toString(universe[3])); + layout.addChild(this.universeBox4); +// new OptionInstance.Enum<>(List.of(true, false), Codec.BOOL).createButton() + layout.addChild(new CycleButton.Builder((enabled) -> + Component.translatable("screen.artnetconfig.enabled", enabled ? "Yes" : "No") + ).withValues(List.of(true, false)).displayOnlyValue().withInitialValue(enabled).create(xCenter, yCenter, 150, 20, Component.translatable("screen.artnetconfig.enabled"), (obj, val) -> { + this.enabled = val; + })); +// layout.addChild(new Button.Builder( +// , +// button -> { +// enabled = !enabled; +// }).pos(xCenter + 40, yCenter + 150) +// .size(100, 20) +// .build() +// ); + layout.addChild( + new Button.Builder(Component.translatable("artneti.save"), button -> this.update()) + .pos(xCenter + 40, yCenter + 200) + .size(150, 20) + .build() + ); + layout.addChild( + new Button.Builder(Component.translatable("gui.back"), button -> { + this.minecraft.setScreen(this.lastScreen); + }) + .pos(xCenter + 40, yCenter + 200) + .size(150, 20) + .build() + ); + layout.arrangeElements(); + layout.visitWidgets(this::addRenderableWidget); + this.repositionElements(); + } + + + protected void repositionElements() { + FrameLayout.centerInRectangle(this.layout, this.getRectangle()); + } + + protected void refresh(){ + + } + + private int getValueFor(int universe){ + return switch (universe) { + case 1 -> Integer.parseInt(this.universeBox1.getValue()); + case 2 -> Integer.parseInt(this.universeBox2.getValue()); + case 3 -> Integer.parseInt(this.universeBox3.getValue()); + case 4 -> Integer.parseInt(this.universeBox4.getValue()); + default -> -1; + }; + } + + private void setValueFor(int universe, int value){ + switch (universe) { + case 1: + TheatricalConfig.INSTANCE.CLIENT.universe1 = value; + break; + case 2: + TheatricalConfig.INSTANCE.CLIENT.universe2 = value; + break; + case 3: + TheatricalConfig.INSTANCE.CLIENT.universe3 = value; + break; + case 4: + TheatricalConfig.INSTANCE.CLIENT.universe4 = value; + break; + }; + } + + private void update(){ + try { + int[] oldUniverses = Arrays.copyOf(universe, 4); + for(int i = 0; i < universe.length; i++){ + int val = getValueFor(i + 1); + universe[i] = val; + setValueFor(i + 1, val); + } + boolean hasChangedIP = false; + if(!Objects.equals(TheatricalConfig.INSTANCE.CLIENT.artNetIP, ipAddressBox.getValue()) && TheatricalConfig.INSTANCE.CLIENT.artNetIP != null){ + hasChangedIP = true; + } + TheatricalConfig.INSTANCE.CLIENT.artNetIP = ipAddressBox.getValue(); + TheatricalConfig.INSTANCE.CLIENT.artnetEnabled = enabled; + ConfigHandler.INSTANCE.saveConfig(ConfigHandler.ConfigSide.CLIENT); + boolean isInGame = Minecraft.getInstance().level != null; + if(isInGame) { + if (!enabled) { + TheatricalClient.getArtNetManager().shutdownAll(); + } else { + if (hasChangedIP) { + TheatricalClient.getArtNetManager().shutdownAll(); + TheatricalClient.getArtNetManager().getClient(); + } + for (int i = 0; i < oldUniverses.length; i++) { + boolean found = false; + if (oldUniverses[i] >= 0) { + for (int x = 0; x < universe.length; x++) { + if (universe[x] == oldUniverses[i]) { + found = true; + break; + } + } + if (!found) { + if (TheatricalClient.getArtNetManager().getClient().isSubscribedTo(oldUniverses[i])) { + TheatricalClient.getArtNetManager().getClient().unsubscribeFromUniverse(oldUniverses[i]); + } + } + } + } + for (int i = 0; i < universe.length; i++) { + if (universe[i] >= 0 && !TheatricalClient.getArtNetManager().getClient().isSubscribedTo(universe[i])) { + TheatricalClient.getArtNetManager().getClient().subscribeToUniverse(universe[i]); + } + } + } + } + this.minecraft.setScreen(this.lastScreen); +// new UpdateArtNetInterface(be.getBlockPos(), ipAddressBox.getValue(), universe).sendToServer(); + } catch(NumberFormatException ignored) { + //We need a nicer way to show that this is invalid? + } + } + + @Override + public void renderBackground(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + super.renderBackground(guiGraphics, mouseX, mouseY, partialTick); + } + + @Override + public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + super.render(guiGraphics, mouseX, mouseY, partialTick); + this.renderLabels(guiGraphics); + } + + + private void renderLabels(GuiGraphics guiGraphics) { +// renderLabel(guiGraphics, "screen.artnetconfig.universe", 0,15, 1); +// renderLabel(guiGraphics, "screen.artnetconfig.universe", 0,15, 2); +// renderLabel(guiGraphics, "screen.artnetconfig.universe", 0,15, 3); +// renderLabel(guiGraphics, "screen.artnetconfig.universe", 0,15, 4); +// renderLabel(guiGraphics, "artneti.ipAddress", 5,10); +// if(!this.be.isOwnedByCurrentClient()){ +// renderLabel(guiGraphics, "artneti.notAuthorized", 5,75); +// } else { +// if(this.be.hasReceivedPacket()){ +// long inSeconds = Math.round((float) (System.currentTimeMillis() - this.be.getLastReceivedPacket()) / 1000); +// renderLabel(guiGraphics, "artneti.lastReceived", 5,75, inSeconds); +// } else { +// renderLabel(guiGraphics, "artneti.notConnected", 5,75); +// } +// } + } + + private void renderLabel(GuiGraphics guiGraphics, String translationKey, int offSetX, int offSetY, Object... replacements){ + MutableComponent translatable = Component.translatable(translationKey, replacements); + guiGraphics.drawString(font, translatable, xCenter + (this.font.width(translatable.getString()) / 2), yCenter + offSetY, 0xffffff, false); + } + + @Override + public void tick() { +// this.dmxUniverse.tick(); +// this.ipAddress.tick(); + } +} \ No newline at end of file diff --git a/common/src/main/java/dev/imabad/theatrical/client/gui/screen/FresnelScreen.java b/common/src/main/java/dev/imabad/theatrical/client/gui/screen/FresnelScreen.java index c327ef9..ccb3b32 100644 --- a/common/src/main/java/dev/imabad/theatrical/client/gui/screen/FresnelScreen.java +++ b/common/src/main/java/dev/imabad/theatrical/client/gui/screen/FresnelScreen.java @@ -19,7 +19,7 @@ public class FresnelScreen extends Screen { private final ResourceLocation GUI = new ResourceLocation(Theatrical.MOD_ID, "textures/gui/blank.png"); private int imageWidth, imageHeight, xCenter, yCenter; - private EditBox dmxAddress; + private EditBox dmxAddress, dmxUniverse; private BasicSlider tiltSlider, panSlider; private FresnelBlockEntity be; @@ -35,8 +35,10 @@ protected void init() { super.init(); xCenter = (this.width - this.imageWidth) / 2; yCenter = (this.height - this.imageHeight) / 2; - this.dmxAddress = new EditBox(this.font, xCenter + 62, yCenter + 25, 50, 10, Component.translatable("fixture.dmxStart")); + this.dmxAddress = new EditBox(this.font, xCenter + (((imageWidth / 2) / 2) - 25), yCenter + 25, 50, 10, Component.translatable("fixture.dmxStart")); this.dmxAddress.setValue(Integer.toString(this.be.getChannelStart())); + this.dmxUniverse = new EditBox(this.font, xCenter + (imageWidth - (((imageWidth / 2) / 2) + 25)), yCenter + 25, 50, 10, Component.translatable("artneti.dmxUniverse")); + this.dmxUniverse.setValue(Integer.toString(this.be.getUniverse())); this.tiltSlider = new BasicSlider(xCenter + 13, yCenter + 45, 150, 20, Component.empty(), be.getTilt(), -90, 90, (newTilt) -> { be.setTilt(newTilt.intValue()); }); @@ -44,6 +46,7 @@ protected void init() { be.setPan(newPan.intValue()); }); this.addRenderableWidget(this.dmxAddress); + this.addRenderableWidget(this.dmxUniverse); this.addRenderableWidget(tiltSlider); this.addRenderableWidget(panSlider); this.addRenderableWidget( @@ -60,7 +63,11 @@ private void update() { if (dmx > 512 || dmx < 0) { return; } - new UpdateDMXFixture(be.getBlockPos(), dmx).sendToServer(); + int universe = Integer.parseInt(this.dmxUniverse.getValue()); + if (universe > 16 || universe < 0) { + return; + } + new UpdateDMXFixture(be.getBlockPos(), dmx, universe).sendToServer(); new UpdateFixturePosition(be.getBlockPos(), be.getTilt(), be.getPan()).sendToServer(); } catch (NumberFormatException ignored) { //We need a nicer way to show that this is invalid? @@ -87,14 +94,15 @@ private void renderWindow(GuiGraphics guiGraphics) { private void renderLabels(GuiGraphics guiGraphics) { renderLabel(guiGraphics, "block.theatrical.led_fresnel", 5, 5); - renderLabel(guiGraphics, "fixture.dmxStart", 0, 15); + renderLabel(guiGraphics, "fixture.dmxStart", -44, 15); + renderLabel(guiGraphics, "artneti.dmxUniverse", 44, 15); renderLabel(guiGraphics, "fixture.tilt", 0, 36); renderLabel(guiGraphics, "fixture.pan", 0, 66); } private void renderLabel(GuiGraphics guiGraphics, String translationKey, int offSetX, int offSetY) { MutableComponent translatable = Component.translatable(translationKey); - guiGraphics.drawString(font, translatable, xCenter + (this.imageWidth / 2) - (this.font.width(translatable.getString()) / 2), yCenter + offSetY, 0x404040, false); + guiGraphics.drawString(font, translatable, xCenter + (this.imageWidth / 2) - (this.font.width(translatable.getString()) / 2) + offSetX, yCenter + offSetY, 0x404040, false); } @Override diff --git a/common/src/main/java/dev/imabad/theatrical/client/gui/screen/GenericDMXConfigurationScreen.java b/common/src/main/java/dev/imabad/theatrical/client/gui/screen/GenericDMXConfigurationScreen.java index f8b2e4b..67b0e58 100644 --- a/common/src/main/java/dev/imabad/theatrical/client/gui/screen/GenericDMXConfigurationScreen.java +++ b/common/src/main/java/dev/imabad/theatrical/client/gui/screen/GenericDMXConfigurationScreen.java @@ -20,7 +20,7 @@ public class GenericDMXConfigurationScreen extends Screen private final int imageHeight; private int xCenter; private int yCenter; - private EditBox dmxAddress; + private EditBox dmxAddress, dmxUniverse; private final T be; private final BlockPos blockPos; private final String titleTranslationKey; @@ -41,7 +41,11 @@ protected void init() { yCenter = (this.height - this.imageHeight) / 2; this.dmxAddress = new EditBox(this.font, xCenter + 62, yCenter + 25, 50, 10, Component.translatable("fixture.dmxStart")); this.dmxAddress.setValue(Integer.toString(this.be.getChannelStart())); - this.addWidget(this.dmxAddress); + this.addRenderableWidget(this.dmxAddress); + + this.dmxUniverse = new EditBox(this.font, xCenter + 62, yCenter + 50, 50, 10, Component.translatable("artneti.dmxUniverse")); + this.dmxUniverse.setValue(Integer.toString(this.be.getUniverse())); + this.addRenderableWidget(this.dmxUniverse); this.addRenderableWidget( new Button.Builder(Component.translatable("artneti.save"), button -> this.update()) .pos(xCenter + 40, yCenter + 90) @@ -56,7 +60,12 @@ private void update(){ if (dmx > 512 || dmx < 0) { return; } - new UpdateDMXFixture(blockPos, dmx).sendToServer(); + + int universe = Integer.parseInt(this.dmxUniverse.getValue()); + if (universe > 16 || universe < 0) { + return; + } + new UpdateDMXFixture(blockPos, dmx, universe).sendToServer(); } catch(NumberFormatException ignored) { //We need a nicer way to show that this is invalid? } @@ -70,7 +79,6 @@ public void renderBackground(GuiGraphics guiGraphics, int mouseX, int mouseY, fl @Override public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { super.render(guiGraphics, mouseX, mouseY, partialTick); - this.dmxAddress.render(guiGraphics, mouseX, mouseY, partialTick); this.renderLabels(guiGraphics); } @@ -83,6 +91,7 @@ private void renderWindow(GuiGraphics guiGraphics){ private void renderLabels(GuiGraphics guiGraphics) { renderLabel(guiGraphics, titleTranslationKey, 5,5); renderLabel(guiGraphics, "fixture.dmxStart", 0,15); + renderLabel(guiGraphics, "artneti.dmxUniverse", 0,40); } private void renderLabel(GuiGraphics guiGraphics, String translationKey, int offSetX, int offSetY){ diff --git a/common/src/main/java/dev/imabad/theatrical/client/gui/widgets/LabeledEditBox.java b/common/src/main/java/dev/imabad/theatrical/client/gui/widgets/LabeledEditBox.java new file mode 100644 index 0000000..b3948fa --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/client/gui/widgets/LabeledEditBox.java @@ -0,0 +1,40 @@ +package dev.imabad.theatrical.client.gui.widgets; + +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Nullable; + +public class LabeledEditBox extends EditBox { + private Font font; + public LabeledEditBox(Font font, int width, int height, Component message) { + super(font, width, height, message); + this.width = width + 10; + this.font = font; + } + + public LabeledEditBox(Font font, int x, int y, int width, int height, Component message) { + super(font, x, y, width, height, message); + this.font = font; + } + + public LabeledEditBox(Font font, int x, int y, int width, int height, @Nullable EditBox editBox, Component message) { + super(font, x, y, width, height, editBox, message); + this.font = font; + } + + @Override + public int getHeight() { + return super.getHeight(); + } + + @Override + public void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + super.renderWidget(guiGraphics, mouseX, mouseY, partialTick); + int k = this.isBordered() ? this.getX() + 4 : this.getX(); + int l = this.isBordered() ? this.getY() + (this.height - 8) / 2 : this.getY(); + + guiGraphics.drawString(font, this.getMessage(), k, l - (font.lineHeight * 2), 0xffffff); + } +} diff --git a/common/src/main/java/dev/imabad/theatrical/config/ConfigHandler.java b/common/src/main/java/dev/imabad/theatrical/config/ConfigHandler.java index 8da4d72..8906159 100644 --- a/common/src/main/java/dev/imabad/theatrical/config/ConfigHandler.java +++ b/common/src/main/java/dev/imabad/theatrical/config/ConfigHandler.java @@ -22,6 +22,13 @@ import java.util.function.Supplier; public class ConfigHandler { + + public static ConfigHandler INSTANCE; + public static ConfigHandler initialize(Path configFolder){ + INSTANCE = new ConfigHandler(configFolder); + return INSTANCE; + } + public enum ConfigSide { COMMON, CLIENT @@ -32,7 +39,7 @@ public enum ConfigSide { private final Map registered_configs = new HashMap<>(); - public ConfigHandler(Path configFolder){ + private ConfigHandler(Path configFolder){ this.configFolder = configFolder; File file = this.configFolder.toFile(); if(!file.exists()){ @@ -62,6 +69,12 @@ public T registerConfig(String modID, ConfigSide side, Su return null; } + public void saveConfig(ConfigSide configSide){ + ResourceLocation resourceLocation = new ResourceLocation(Theatrical.MOD_ID, configSide.name().toLowerCase(Locale.ENGLISH)); + File sideConfig = Paths.get(this.configFolder.toString(), Theatrical.MOD_ID + "-" + configSide.name().toLowerCase(Locale.ENGLISH) + ".yml").toFile(); + save(registered_configs.get(resourceLocation), sideConfig); + } + private void save(T config, File output){ Map yamlMap = new HashMap<>(); magicWrite(config, yamlMap); diff --git a/common/src/main/java/dev/imabad/theatrical/config/TheatricalConfig.java b/common/src/main/java/dev/imabad/theatrical/config/TheatricalConfig.java index 8d5a1ed..a638088 100644 --- a/common/src/main/java/dev/imabad/theatrical/config/TheatricalConfig.java +++ b/common/src/main/java/dev/imabad/theatrical/config/TheatricalConfig.java @@ -24,6 +24,20 @@ public static class ClientConfig extends BaseConfig{ @TheatricalConfigItem public boolean doOwnerCheck = true; + + @TheatricalConfigItem + public boolean artnetEnabled = false; + @TheatricalConfigItem + public String artNetIP = ""; + @TheatricalConfigItem(minValue = "0", maxValue = "16") + public int universe1 = 1; + @TheatricalConfigItem(minValue = "-1", maxValue = "16") + public int universe2 = -1; + @TheatricalConfigItem(minValue = "-1", maxValue = "16") + public int universe3 = -1; + @TheatricalConfigItem(minValue = "-1", maxValue = "16") + public int universe4 = -1; + } public static class ServerConfig extends BaseConfig{ diff --git a/common/src/main/java/dev/imabad/theatrical/dmx/DMXDevice.java b/common/src/main/java/dev/imabad/theatrical/dmx/DMXDevice.java new file mode 100644 index 0000000..89357c3 --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/dmx/DMXDevice.java @@ -0,0 +1,78 @@ +package dev.imabad.theatrical.dmx; + +import ch.bildspur.artnet.rdm.RDMDeviceId; +import net.minecraft.resources.ResourceLocation; + +public class DMXDevice { + + private RDMDeviceId deviceId; + private int dmxStartAddress, dmxChannelCount, deviceTypeId, activePersonality; + private String modelName; + private ResourceLocation fixtureID; + + public DMXDevice(RDMDeviceId deviceId, int dmxStartAddress, int dmxChannelCount, int deviceTypeId, int activePersonality, String modelName, ResourceLocation fixtureID) { + this.deviceId = deviceId; + this.dmxStartAddress = dmxStartAddress; + this.dmxChannelCount = dmxChannelCount; + this.deviceTypeId = deviceTypeId; + this.activePersonality = activePersonality; + this.modelName = modelName; + this.fixtureID = fixtureID; + } + + public int getDmxStartAddress() { + return dmxStartAddress; + } + + public int getDmxChannelCount() { + return dmxChannelCount; + } + + public void setDmxStartAddress(int dmxStartAddress) { + this.dmxStartAddress = dmxStartAddress; + } + + public void setDmxChannelCount(int dmxChannelCount) { + this.dmxChannelCount = dmxChannelCount; + } + + public int getDeviceTypeId() { + return deviceTypeId; + } + + public void setDeviceTypeId(int deviceTypeId) { + this.deviceTypeId = deviceTypeId; + } + + public String getModelName() { + return modelName; + } + + public void setModelName(String modelName) { + this.modelName = modelName; + } + + public int getActivePersonality() { + return activePersonality; + } + + public ResourceLocation getFixtureID() { + return fixtureID; + } + + public void setActivePersonality(int activePersonality) { + this.activePersonality = activePersonality; + } + + public void setFixtureID(ResourceLocation fixtureID) { + this.fixtureID = fixtureID; + } + + public RDMDeviceId getDeviceId() { + return deviceId; + } + + public void setDeviceId(RDMDeviceId deviceId) { + this.deviceId = deviceId; + } +} diff --git a/common/src/main/java/dev/imabad/theatrical/dmx/DMXNetworkData.java b/common/src/main/java/dev/imabad/theatrical/dmx/DMXNetworkData.java index 9a389b5..b344465 100644 --- a/common/src/main/java/dev/imabad/theatrical/dmx/DMXNetworkData.java +++ b/common/src/main/java/dev/imabad/theatrical/dmx/DMXNetworkData.java @@ -1,32 +1,73 @@ package dev.imabad.theatrical.dmx; +import ch.bildspur.artnet.rdm.RDMDeviceId; import dev.imabad.theatrical.api.dmx.DMXConsumer; +import dev.imabad.theatrical.net.artnet.NotifyConsumerChange; +import io.netty.util.collection.IntObjectHashMap; +import io.netty.util.collection.IntObjectMap; import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; +import java.util.*; public class DMXNetworkData { private static final DMXNetworkData INSTANCE = new DMXNetworkData(); public static DMXNetworkData getInstance(){ return INSTANCE; } - private final Map dmxNodes = new HashMap<>(); + private final Set knownSenders = new HashSet<>(); + private final IntObjectMap> universeToNodeMap = new IntObjectHashMap<>(); public void addConsumer(BlockPos pos, DMXConsumer consumer){ - dmxNodes.put(pos, consumer); + Map universe = universeToNodeMap.computeIfAbsent(consumer.getUniverse(), (uni) -> new HashMap<>()); + universe.put(pos, consumer); + universeToNodeMap.put(consumer.getUniverse(), universe); + new NotifyConsumerChange(consumer.getUniverse(), + NotifyConsumerChange.ChangeType.ADD, + new DMXDevice( + consumer.getDeviceId(), consumer.getChannelStart(), consumer.getChannelCount(), + consumer.getDeviceTypeId(), consumer.getActivePersonality(), consumer.getModelName(), consumer.getFixtureId())) + .sendTo(knownSenders); } - public void removeConsumer(BlockPos pos){ - dmxNodes.remove(pos); + + public void updateConsumer(DMXConsumer consumer){ + new NotifyConsumerChange(consumer.getUniverse(), + NotifyConsumerChange.ChangeType.UPDATE, + new DMXDevice(consumer.getDeviceId(), consumer.getChannelStart(), consumer.getChannelCount(), + consumer.getDeviceTypeId(),consumer.getActivePersonality(), consumer.getModelName(), consumer.getFixtureId())) + .sendTo(knownSenders); + } + + public void removeConsumer(DMXConsumer consumer, BlockPos pos){ + if(!universeToNodeMap.containsKey(consumer.getUniverse())){ + return; + } + Map universe = universeToNodeMap.get(consumer.getUniverse()); + universe.remove(pos); + new NotifyConsumerChange(consumer.getUniverse(), NotifyConsumerChange.ChangeType.REMOVE, + new DMXDevice(consumer.getDeviceId(), 0, 0,0, + 0, "", new ResourceLocation(""))) + .sendTo(knownSenders); } - public Collection getConsumers(){ - return dmxNodes.values(); + public Collection getConsumers(int universe){ + return universeToNodeMap.get(universe).values(); + } + + public BlockPos getConsumerPos(int universe, RDMDeviceId deviceId){ + for (Map.Entry blockPosDMXConsumerEntry : universeToNodeMap.get(universe).entrySet()) { + if(blockPosDMXConsumerEntry.getValue().getDeviceId().equals(deviceId)){ + return blockPosDMXConsumerEntry.getKey(); + } + } + return null; } - public Collection getConsumersInRange(BlockPos fromPos, int radius){ + public Collection getConsumersInRange(int universe, BlockPos fromPos, int radius){ Collection consumers = new HashSet<>(); - for(Map.Entry entry : dmxNodes.entrySet()){ + if(!universeToNodeMap.containsKey(universe) || universeToNodeMap.get(universe).isEmpty()){ + return consumers; + } + for(Map.Entry entry : universeToNodeMap.get(universe).entrySet()){ if(Math.sqrt(fromPos.distToCenterSqr(entry.getKey().getX(), entry.getKey().getY(), entry.getKey().getZ())) <= radius){ consumers.add(entry.getValue()); } @@ -34,4 +75,19 @@ public Collection getConsumersInRange(BlockPos fromPos, int radius) return consumers; } + public Set getUniverses(){ + return universeToNodeMap.keySet(); + } + + public void addKnownSender(ServerPlayer player){ + this.knownSenders.add(player); + } + + public void removeKnownSender(ServerPlayer player){ + this.knownSenders.remove(player); + } + + public boolean isKnownSender(ServerPlayer player){ + return this.knownSenders.contains(player); + } } diff --git a/common/src/main/java/dev/imabad/theatrical/dmx/TheatricalArtNetClient.java b/common/src/main/java/dev/imabad/theatrical/dmx/TheatricalArtNetClient.java index dfd65c5..04b93fb 100644 --- a/common/src/main/java/dev/imabad/theatrical/dmx/TheatricalArtNetClient.java +++ b/common/src/main/java/dev/imabad/theatrical/dmx/TheatricalArtNetClient.java @@ -1,48 +1,479 @@ package dev.imabad.theatrical.dmx; -import ch.bildspur.artnet.ArtNetBuffer; -import ch.bildspur.artnet.ArtNetClient; -import ch.bildspur.artnet.ArtNetException; +import ch.bildspur.artnet.*; import ch.bildspur.artnet.events.ArtNetServerEventAdapter; -import ch.bildspur.artnet.packets.ArtDmxPacket; -import ch.bildspur.artnet.packets.ArtNetPacket; +import ch.bildspur.artnet.packets.*; +import ch.bildspur.artnet.rdm.RDMCommandClass; +import ch.bildspur.artnet.rdm.RDMDeviceId; +import ch.bildspur.artnet.rdm.RDMPacket; +import ch.bildspur.artnet.rdm.RDMParameter; +import dev.imabad.theatrical.Constants; +import dev.imabad.theatrical.TheatricalExpectPlatform; +import dev.imabad.theatrical.api.Fixture; +import dev.imabad.theatrical.api.dmx.DMXPersonality; +import dev.imabad.theatrical.api.dmx.DMXSlot; +import dev.imabad.theatrical.config.TheatricalConfig; +import dev.imabad.theatrical.fixtures.Fixtures; +import dev.imabad.theatrical.net.artnet.RDMUpdateConsumer; +import dev.imabad.theatrical.net.artnet.RequestConsumers; +import dev.imabad.theatrical.net.artnet.SendArtNetData; +import dev.imabad.theatrical.util.ByteUtils; +import io.netty.util.collection.IntObjectHashMap; +import io.netty.util.collection.IntObjectMap; +import net.minecraft.client.Minecraft; import java.net.InetAddress; import java.net.SocketException; - -import static ch.bildspur.artnet.packets.PacketType.ART_OUTPUT; +import java.nio.ByteBuffer; +import java.util.*; public class TheatricalArtNetClient extends ArtNetClient { + private InetAddress address; + private final byte[] RDM_DEVICE_ID; private boolean isRunning = false; private long lastPacketMS = 0; + private IntObjectMap> proxiedDevices; + private boolean listChanged = false; + private Queue queuedMessages; + private int[] universes = new int[]{-1, -1, -1, -1}; - public TheatricalArtNetClient() { + public TheatricalArtNetClient(InetAddress address) { super(); + this.address = address; + RDM_DEVICE_ID = buildDeviceId(); + proxiedDevices = new IntObjectHashMap<>(); + queuedMessages = new ArrayDeque<>(); + universes = new int[]{TheatricalConfig.INSTANCE.CLIENT.universe1,TheatricalConfig.INSTANCE.CLIENT.universe2,TheatricalConfig.INSTANCE.CLIENT.universe3,TheatricalConfig.INSTANCE.CLIENT.universe4}; + for (int i = 0; i < universes.length; i++) { + new RequestConsumers(i).sendToServer(); + } } public TheatricalArtNetClient(ArtNetBuffer inputBuffer) { super(inputBuffer); + RDM_DEVICE_ID = buildDeviceId(); } public TheatricalArtNetClient(ArtNetBuffer inputBuffer, int serverPort, int clientPort) { super(inputBuffer, serverPort, clientPort); + RDM_DEVICE_ID = buildDeviceId(); } - private void onPacketReceived(final ArtNetPacket packet) { - // only store input data if buffer is created - if (getInputBuffer() == null) + private byte[] buildDeviceId(){ + byte[] deviceID = Arrays.copyOfRange(ByteUtils.longToBytes(Minecraft.getInstance().getGameProfile().getId().getLeastSignificantBits()), 0, 4); + ByteBuffer wrap = ByteBuffer.wrap(new byte[6]); + wrap.putShort(Constants.MANUFACTURER_ID); + wrap.put(deviceID); + return wrap.array(); + } + + public boolean isSubscribedTo(int universe){ + for (int univers : universes) { + if (univers == universe) { + return true; + } + } + return false; + } + + public boolean subscribeToUniverse(int universe){ + // Prevent duplicates! + for (int univers : universes) { + if (univers == universe) { + return false; + } + } + for (int i = 0; i < universes.length; i++) { + if(universes[i] == -1){ + universes[i] = universe; + buildAndSetPollReply(); + return true; + } + } + return false; + } + + public boolean unsubscribeFromUniverse(int universe){ + for(int i = 0; i getProxyMap(int universe){ + if(!proxiedDevices.containsKey(universe)){ + proxiedDevices.put(universe, new HashMap<>()); + } + return proxiedDevices.get(universe); + } + + public void addDevice(int universe, RDMDeviceId deviceId, DMXDevice dmxDevice){ + if(getProxyMap(universe).containsKey(deviceId)){ return; + } + getProxyMap(universe).put(deviceId, dmxDevice); + listChanged = true; + sendTOD(universe); + //queueProxiedDevicesUpdate(); + } + public void updateDevice(int universe, RDMDeviceId deviceId, DMXDevice dmxDevice){ + getProxyMap(universe).put(deviceId, dmxDevice); + } - if (packet.getType() != ART_OUTPUT) + public void clearDevices(){ + this.proxiedDevices.clear(); + } + + public void removeDevice(int universe, RDMDeviceId deviceId){ + if(!getProxyMap(universe).containsKey(deviceId)){ return; + } + getProxyMap(universe).remove(deviceId); + listChanged = true; + sendTOD(universe); +// queueProxiedDevicesUpdate(); + } + + private void queueProxiedDevicesUpdate(){ +// RDMPacket getCommandResponse = new RDMPacket(); +// getCommandResponse.setParameter(RDMParameter.PROXIED_DEVICES_COUNT); +// getCommandResponse.setParameterDataLength(0x03); +// ch.bildspur.artnet.packets.ByteUtils byteBuffer = new ch.bildspur.artnet.packets.ByteUtils(new byte[0x03]); +// byteBuffer.setInt16(proxiedDevices.size() & 0xFFFF, 0); +// byteBuffer.setInt8(listChanged ? 1 : 0, 2); +// getCommandResponse.setParameterData(byteBuffer.getBytes()); +// queuedMessages.add(getCommandResponse); + } + + private void sendTOD(int universe){ + ArtTodDataPacket replyPacket = new ArtTodDataPacket(); + replyPacket.setTotalDevices(proxiedDevices.size() + 1); + replyPacket.setUniverse(0, universe); +// replyPacket.addDevice(RDM_DEVICE_ID); + for (RDMDeviceId proxiedDevice : getProxyMap(universe).keySet()) { + replyPacket.addDevice(proxiedDevice.toBytes()); + } + getArtNetServer().broadcastPacket(replyPacket); + } + + private void onPacketReceived(InetAddress sourceAddress, final ArtNetPacket packet) { + switch(packet.getType()){ + case ART_OUTPUT: { + if (getInputBuffer() == null) + return; - ArtDmxPacket dmxPacket = (ArtDmxPacket) packet; - int subnet = dmxPacket.getSubnetID(); - int universe = dmxPacket.getUniverseID(); - lastPacketMS = System.currentTimeMillis(); + ArtDmxPacket dmxPacket = (ArtDmxPacket) packet; + int subnet = dmxPacket.getSubnetID(); + int universe = dmxPacket.getUniverseID(); + lastPacketMS = System.currentTimeMillis(); - getInputBuffer().setDmxData((short) subnet, (short) universe, dmxPacket.getDmxData()); + getInputBuffer().setDmxData((short) subnet, (short) universe, dmxPacket.getDmxData()); + new SendArtNetData(universe, dmxPacket.getDmxData()).sendToServer(); + break; + } + case ART_TOD_REQUEST: { + ArtTodRequestPacket requestPacket = (ArtTodRequestPacket) packet; + sendTOD(requestPacket.getUniverseID()); + break; + } + case ART_TOD_CONTROL: { + ArtTodControlPacket controlPacket = (ArtTodControlPacket) packet; + if (controlPacket.isFlush()) { + sendTOD(controlPacket.getUniverseID()); + } + break; + } + case ART_RDM: + ArtRdmPacket artRdmPacket = (ArtRdmPacket) packet; + RDMPacket rdmPacket = artRdmPacket.getRdmPacket(); + int universe = getUniverseFromPortAddress(artRdmPacket.getAddress()); + System.out.println("RECV: " + rdmPacket); + RDMDeviceId destinationID = new RDMDeviceId(rdmPacket.getDestinationID()); + if(!getProxyMap(universe).containsKey(destinationID) && !Arrays.equals(rdmPacket.getDestinationID(), RDM_DEVICE_ID)){ + return; + } + if(rdmPacket.getCommandClass() != null && rdmPacket.getParameter() != null) { + switch (rdmPacket.getCommandClass()) { + case GET_COMMAND: + { + RDMPacket getCommandResponse = new RDMPacket(); + getCommandResponse.setDestinationID(rdmPacket.getSourceID()); + getCommandResponse.setSourceID(rdmPacket.getDestinationID()); + getCommandResponse.setTransactionID(rdmPacket.getTransactionID()); + getCommandResponse.setPortID(0x00); + getCommandResponse.setMessageCount(queuedMessages.size()); + getCommandResponse.setSubDevice((short) 0); + getCommandResponse.setCommandClass(RDMCommandClass.GET_COMMAND_RESPONSE); + switch (rdmPacket.getParameter()) { + case SUPPORTED_PARAMETERS: + getCommandResponse.setParameter(RDMParameter.SUPPORTED_PARAMETERS); + RDMParameter[] supportedParameters = RDMParameter.values(); + int dataLength = supportedParameters.length * 2; + ByteBuffer supportedParameterIDs = ByteBuffer.wrap(new byte[dataLength]); + for (RDMParameter supportedParameter : supportedParameters) { + supportedParameterIDs.putShort((short) supportedParameter.getId()); + } + getCommandResponse.setParameterDataLength(dataLength); + getCommandResponse.setParameterData(supportedParameterIDs.array()); + break; + case DEVICE_INFO: + getCommandResponse.setParameter(RDMParameter.DEVICE_INFO); + ch.bildspur.artnet.packets.ByteUtils deviceInfoData = new ch.bildspur.artnet.packets.ByteUtils(new byte[0x13]); + deviceInfoData.setInt8(0x01, 0); + deviceInfoData.setInt8(0x00, 1); + if (!Arrays.equals(rdmPacket.getDestinationID(), RDM_DEVICE_ID)) { + DMXDevice dmxDevice = getProxyMap(universe).get(destinationID); + Fixture fixture = Fixtures.FIXTURES.get(dmxDevice.getFixtureID()); + deviceInfoData.setInt16(dmxDevice.getDeviceTypeId(), 2); + deviceInfoData.setInt16(0x01 >> 8, 4); + deviceInfoData.setInt16(dmxDevice.getDmxChannelCount(), 10); + deviceInfoData.setInt8(dmxDevice.getActivePersonality() + 1, 12); + deviceInfoData.setInt8(fixture.getDMXPersonalities().size(), 13); + deviceInfoData.setInt16(dmxDevice.getDmxStartAddress(), 14); + } else { + deviceInfoData.setInt16(0x0, 2); + deviceInfoData.setInt16(0x08 >> 8, 4); + deviceInfoData.setInt16(0, 10); + deviceInfoData.setInt16(0xFFFF, 14); + } + ByteBuffer versionId = ByteBuffer.wrap(new byte[4]); + versionId.putInt(1); + deviceInfoData.setByteChunk(versionId.array(), 6); + deviceInfoData.setInt16(0, 16); + deviceInfoData.setInt8(0, 18); + getCommandResponse.setParameterDataLength(0x13); + getCommandResponse.setParameterData(deviceInfoData.getBytes()); + break; + case DMX_START_ADDRESS: { + getCommandResponse.setParameter(RDMParameter.DMX_START_ADDRESS); + ch.bildspur.artnet.packets.ByteUtils dmxStartAddress = new ch.bildspur.artnet.packets.ByteUtils(new byte[0x02]); + if (!Arrays.equals(rdmPacket.getDestinationID(), RDM_DEVICE_ID)) { + DMXDevice dmxDevice = getProxyMap(universe).get(destinationID); + dmxStartAddress.setInt16(dmxDevice.getDmxStartAddress(), 0); + } else { + dmxStartAddress.setInt16(0, 0); + } + getCommandResponse.setParameterDataLength(dmxStartAddress.length); + getCommandResponse.setParameterData(dmxStartAddress.getBytes()); + break; + } + case SOFTWARE_VERSION_LABEL: + getCommandResponse.setParameter(RDMParameter.SOFTWARE_VERSION_LABEL); + String modVersion = TheatricalExpectPlatform.getModVersion(); + if (modVersion.length() > 32) { + modVersion = modVersion.substring(0, 32); + } + byte[] modVersionBytes = modVersion.getBytes(); + getCommandResponse.setParameterDataLength(modVersionBytes.length); + getCommandResponse.setParameterData(modVersionBytes); + break; + case IDENTIFY_DEVICE: + getCommandResponse.setParameter(RDMParameter.IDENTIFY_DEVICE); + getCommandResponse.setParameterDataLength(0); + getCommandResponse.setParameterData(new byte[0]); + break; + case MANUFACTURER_LABEL: + getCommandResponse.setParameter(RDMParameter.MANUFACTURER_LABEL); + byte[] manufacturerLabelBytes = "Theatrical".getBytes(); + getCommandResponse.setParameterDataLength(manufacturerLabelBytes.length); + getCommandResponse.setParameterData(manufacturerLabelBytes); + break; + case DEVICE_MODEL_DESCRIPTION: + getCommandResponse.setParameter(RDMParameter.DEVICE_MODEL_DESCRIPTION); + String deviceModel = ""; + if (!Arrays.equals(rdmPacket.getDestinationID(), RDM_DEVICE_ID)) { + DMXDevice dmxDevice = getProxyMap(universe).get(destinationID); + deviceModel = dmxDevice.getModelName(); + } + if (deviceModel.length() > 32) { + deviceModel = deviceModel.substring(0, 32); + } + byte[] deviceModelBytes = deviceModel.getBytes(); + getCommandResponse.setParameterDataLength(deviceModelBytes.length); + getCommandResponse.setParameterData(deviceModelBytes); + break; + case DMX_PERSONALITY: { + getCommandResponse.setParameter(RDMParameter.DMX_PERSONALITY); + ch.bildspur.artnet.packets.ByteUtils byteUtils = new ch.bildspur.artnet.packets.ByteUtils(new byte[2]); + if (!Arrays.equals(rdmPacket.getDestinationID(), RDM_DEVICE_ID)) { + DMXDevice dmxDevice = getProxyMap(universe).get(destinationID); + Fixture fixture = Fixtures.FIXTURES.get(dmxDevice.getFixtureID()); + int activePersonality = dmxDevice.getActivePersonality() + 1; + byteUtils.setInt8(activePersonality, 0); + byteUtils.setInt8(fixture.getDMXPersonalities().size(), 1); + } + getCommandResponse.setParameterDataLength(byteUtils.length); + getCommandResponse.setParameterData(byteUtils.getBytes()); + break; + } + case DMX_PERSONALITY_DESCRIPTION:{ + getCommandResponse.setParameter(RDMParameter.DMX_PERSONALITY_DESCRIPTION); + String personalityText = ""; + int footprint = 0; + if (!Arrays.equals(rdmPacket.getDestinationID(), RDM_DEVICE_ID)) { + DMXDevice dmxDevice = getProxyMap(universe).get(destinationID); + Fixture fixture = Fixtures.FIXTURES.get(dmxDevice.getFixtureID()); + personalityText = fixture.getDMXPersonalities() + .get(dmxDevice.getActivePersonality()).getDescription(); + footprint = dmxDevice.getDmxChannelCount(); + } + if (personalityText.length() > 32) { + personalityText = personalityText.substring(0, 32); + } + byte[] personalityBytes = personalityText.getBytes(); + ch.bildspur.artnet.packets.ByteUtils byteUtils = new ch.bildspur.artnet.packets.ByteUtils(new byte[personalityBytes.length + 3]); + byteUtils.setInt8(rdmPacket.getParameterData()[0], 0); + byteUtils.setInt16(footprint, 1); + byteUtils.setByteChunk(personalityBytes, 3); + getCommandResponse.setParameterDataLength(byteUtils.length); + getCommandResponse.setParameterData(byteUtils.getBytes()); + break; + } + case SLOT_INFO: { + getCommandResponse.setParameter(RDMParameter.SLOT_INFO); + ch.bildspur.artnet.packets.ByteUtils byteUtils; + if(!Arrays.equals(rdmPacket.getDestinationID(), RDM_DEVICE_ID)) { + DMXDevice dmxDevice = getProxyMap(universe).get(destinationID); + Fixture fixture = Fixtures.FIXTURES.get(dmxDevice.getFixtureID()); + DMXPersonality activePersonality = fixture.getDMXPersonalities() + .get(dmxDevice.getActivePersonality()); + byteUtils = new ch.bildspur.artnet.packets.ByteUtils(new byte[activePersonality.getSlots().size() * 5]); + for (int i = 0; i < activePersonality.getSlots().size(); i++) { + DMXSlot dmxSlot = activePersonality.getSlots().get(i); + byteUtils.setInt16(i, (i * 5)); + byteUtils.setInt8(dmxSlot.slotType().getId(), (i * 5) + 2); + byteUtils.setInt16(dmxSlot.slotID().getId(), (i * 5) + 3); + } + } else { + byteUtils = new ch.bildspur.artnet.packets.ByteUtils(new byte[0]); + } + getCommandResponse.setParameterDataLength(byteUtils.length); + getCommandResponse.setParameterData(byteUtils.getBytes()); + break; + } + case SLOT_DESCRIPTION: { + getCommandResponse.setParameter(RDMParameter.SLOT_DESCRIPTION); + ch.bildspur.artnet.packets.ByteUtils byteUtils; + if(!Arrays.equals(rdmPacket.getDestinationID(), RDM_DEVICE_ID)) { + DMXDevice dmxDevice = getProxyMap(universe).get(destinationID); + Fixture fixture = Fixtures.FIXTURES.get(dmxDevice.getFixtureID()); + DMXPersonality activePersonality = fixture.getDMXPersonalities() + .get(dmxDevice.getActivePersonality()); + int slotRequested = new ch.bildspur.artnet.packets.ByteUtils(rdmPacket.getParameterData()).getInt16(0); + DMXSlot dmxSlot1 = activePersonality.getSlots().get(slotRequested); + byte[] slotLabelBytes = dmxSlot1.label().getBytes(); + byteUtils = new ch.bildspur.artnet.packets.ByteUtils(new byte[2 + (slotLabelBytes.length)]); + byteUtils.setInt16(slotRequested, 0); + byteUtils.setByteChunk(slotLabelBytes, 2); + } else { + byteUtils = new ch.bildspur.artnet.packets.ByteUtils(new byte[0]); + } + getCommandResponse.setParameterDataLength(byteUtils.length); + getCommandResponse.setParameterData(byteUtils.getBytes()); + break; + } + case DEFAULT_SLOT_VALUE: { + getCommandResponse.setParameter(RDMParameter.DEFAULT_SLOT_VALUE); + ch.bildspur.artnet.packets.ByteUtils byteUtils; + if(!Arrays.equals(rdmPacket.getDestinationID(), RDM_DEVICE_ID)) { + DMXDevice dmxDevice = getProxyMap(universe).get(destinationID); + Fixture fixture = Fixtures.FIXTURES.get(dmxDevice.getFixtureID()); + DMXPersonality activePersonality = fixture.getDMXPersonalities() + .get(dmxDevice.getActivePersonality()); + byteUtils = new ch.bildspur.artnet.packets.ByteUtils(new byte[activePersonality.getSlots().size() * 3]); + for (int i = 0; i < activePersonality.getSlots().size(); i++) { + byteUtils.setInt16(i, (i * 3)); + byteUtils.setInt8(0, (i * 3) + 2); + } + } else { + byteUtils = new ch.bildspur.artnet.packets.ByteUtils(new byte[0]); + } + getCommandResponse.setParameterDataLength(byteUtils.length); + getCommandResponse.setParameterData(byteUtils.getBytes()); + break; + } +// case PROXIED_DEVICES_COUNT: +// getCommandResponse.setParameter(RDMParameter.PROXIED_DEVICES_COUNT); +// getCommandResponse.setParameterDataLength(0x03); +// ch.bildspur.artnet.packets.ByteUtils byteBuffer = new ch.bildspur.artnet.packets.ByteUtils(new byte[0x03]); +// byteBuffer.setInt16(proxiedDevices.size() & 0xFFFF, 0); +// byteBuffer.setInt8(listChanged ? 1 : 0, 2); +// getCommandResponse.setParameterData(byteBuffer.getBytes()); +// listChanged = false; +// break; + case QUEUED_MESSAGE: + if(queuedMessages.isEmpty()){ + getCommandResponse.setParameter(RDMParameter.STATUS_MESSAGES); + getCommandResponse.setParameterDataLength(0); + break; + } + RDMPacket queuedMessage = queuedMessages.poll(); + getCommandResponse.setParameter(queuedMessage.getParameter()); + getCommandResponse.setParameterDataLength(queuedMessage.getParameterDataLength()); + getCommandResponse.setParameterData(queuedMessage.getParameterData()); + break; + default: + return; + } + getCommandResponse.write(); + System.out.println("SEND: " + getCommandResponse); + ArtRdmPacket sendArtRdmPacket = new ArtRdmPacket(); + sendArtRdmPacket.setRdmPacket(getCommandResponse); + sendArtRdmPacket.setNet(0); + sendArtRdmPacket.setAddress(0 << 4 | universe); + sendArtRdmPacket.write(); + getArtNetServer().unicastPacket(sendArtRdmPacket, sourceAddress); + return; + } + case SET_COMMAND: { + RDMPacket setCommandResponse = new RDMPacket(); + setCommandResponse.setDestinationID(rdmPacket.getSourceID()); + setCommandResponse.setSourceID(rdmPacket.getDestinationID()); + setCommandResponse.setTransactionID(rdmPacket.getTransactionID()); + setCommandResponse.setPortID(0x00); + setCommandResponse.setMessageCount(queuedMessages.size()); + setCommandResponse.setSubDevice((short) 0); + setCommandResponse.setCommandClass(RDMCommandClass.SET_COMMAND_RESPONSE); + DMXDevice targetDevice = getProxyMap(universe).get(new RDMDeviceId(rdmPacket.getDestinationID())); + if(targetDevice == null){ + return; + } + switch (rdmPacket.getParameter()) { + case DMX_START_ADDRESS: { + setCommandResponse.setParameter(RDMParameter.DMX_START_ADDRESS); + ch.bildspur.artnet.packets.ByteUtils inData = new ch.bildspur.artnet.packets.ByteUtils(rdmPacket.getParameterData()); + int newAddress = inData.getInt16(0); + new RDMUpdateConsumer(universe, targetDevice.getDeviceId(), newAddress).sendToServer(); + break; + } + default: + break; + } + setCommandResponse.write(); + System.out.println("SEND: " + setCommandResponse); + ArtRdmPacket sendArtRdmPacket = new ArtRdmPacket(); + sendArtRdmPacket.setRdmPacket(setCommandResponse); + sendArtRdmPacket.setNet(0); + sendArtRdmPacket.setAddress(0 << 4 | universe); + sendArtRdmPacket.write(); + getArtNetServer().unicastPacket(sendArtRdmPacket, sourceAddress); + return; + } + } + } + break; + } + // only store input data if buffer is created + } + + private int getUniverseFromPortAddress(int portAddress) + { + return portAddress & 0xF; } public boolean hasReceivedPacket(){ @@ -65,11 +496,17 @@ public void start(InetAddress networkInterfaceAddress) { getArtNetServer().addListener( new ArtNetServerEventAdapter() { @Override - public void artNetPacketReceived(ArtNetPacket packet) { - onPacketReceived(packet); + public void artNetPacketReceived(InetAddress sourceAddress, ArtNetPacket packet) { + try { + onPacketReceived(sourceAddress, packet); + } + catch (Exception e) { + e.printStackTrace(); + } } }); - + getArtNetServer().setBroadcastAddress("127.0.0.255"); + buildAndSetPollReply(); getArtNetServer().start(networkInterfaceAddress); isRunning = true; @@ -78,6 +515,38 @@ public void artNetPacketReceived(ArtNetPacket packet) { } } + private void buildAndSetPollReply(){ + ArtPollReplyPacket defaultReplyPacket = new ArtPollReplyPacket(); + defaultReplyPacket.setIp(address); + defaultReplyPacket.setLongName("Theatrical - " + Minecraft.getInstance().getGameProfile().getName()); + defaultReplyPacket.setShortName("Theatrical"); + defaultReplyPacket.setNodeStyle(NodeStyle.ST_NODE); + defaultReplyPacket.setEstaManufacturerCode(0x7ff0); + byte nodeStatus = (byte) 0; + nodeStatus |= (byte) (3 << 6); + nodeStatus |= 3 << 4; + nodeStatus |= 0b00000010; + defaultReplyPacket.setNodeStatus(nodeStatus); + PortDescriptor[] ports = new PortDescriptor[4]; + int portCount = 0; + for(int i = 0; i < universes.length; i++){ + int universe = universes[i]; + if(universe == -1){ + ports[i] = new PortDescriptor(false, false, PortType.DMX512, (byte) 0 ,(byte) 0, 0, 0); + } else { + portCount++; + ports[i] = new PortDescriptor(true, false, PortType.DMX512, (byte) 0 ,(byte) 0, 0, universe); + } + } + defaultReplyPacket.setNumPorts(portCount); + defaultReplyPacket.setPorts(ports); + defaultReplyPacket.setVersionInfo(1); + defaultReplyPacket.setSubSwitch(0); + defaultReplyPacket.setOemCode(0x0007); + defaultReplyPacket.translateData(); + getArtNetServer().setDefaultReplyPacket(defaultReplyPacket); + } + @Override public boolean isRunning() { return this.isRunning; diff --git a/common/src/main/java/dev/imabad/theatrical/fixtures/Fixtures.java b/common/src/main/java/dev/imabad/theatrical/fixtures/Fixtures.java index 1eed329..cc32e9c 100644 --- a/common/src/main/java/dev/imabad/theatrical/fixtures/Fixtures.java +++ b/common/src/main/java/dev/imabad/theatrical/fixtures/Fixtures.java @@ -20,6 +20,11 @@ public class Fixtures { public static final RegistrySupplier LED_PANEL = FIXTURES.register(new ResourceLocation(Theatrical.MOD_ID, "led_panel"), LEDPanelFixture::new); + + public static final RegistrySupplier REDSTONE_INTERFACE = FIXTURES.register( + new ResourceLocation(Theatrical.MOD_ID, "redstone_interface"), + RedstoneInterfaceFixture::new + ); public static void init() { // NOOP } diff --git a/common/src/main/java/dev/imabad/theatrical/fixtures/LEDFresnelFixture.java b/common/src/main/java/dev/imabad/theatrical/fixtures/LEDFresnelFixture.java index a17ffc9..ac7efc6 100644 --- a/common/src/main/java/dev/imabad/theatrical/fixtures/LEDFresnelFixture.java +++ b/common/src/main/java/dev/imabad/theatrical/fixtures/LEDFresnelFixture.java @@ -1,15 +1,32 @@ package dev.imabad.theatrical.fixtures; +import ch.bildspur.artnet.rdm.RDMSlotID; +import ch.bildspur.artnet.rdm.RDMSlotType; import dev.imabad.theatrical.Theatrical; import dev.imabad.theatrical.api.Fixture; import dev.imabad.theatrical.api.HangType; +import dev.imabad.theatrical.api.dmx.DMXPersonality; +import dev.imabad.theatrical.api.dmx.DMXSlot; import dev.imabad.theatrical.blocks.light.BaseLightBlock; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.state.BlockState; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + public class LEDFresnelFixture extends Fixture { + private static final List PERSONALITIES = Collections.singletonList( + new DMXPersonality(4, "4-Channel Mode") + .addSlot(SharedSlots.INTENSITY) + .addSlot(SharedSlots.RED) + .addSlot(SharedSlots.GREEN) + .addSlot(SharedSlots.BLUE) + ); + private static final ResourceLocation TILT_MODEL = new ResourceLocation(Theatrical.MOD_ID, "block/fresnel/fresnel_body_only"); private static final ResourceLocation PAN_MODEL = new ResourceLocation(Theatrical.MOD_ID, "block/fresnel/fresnel_handle_only"); private static final ResourceLocation STATIC_MODEL = new ResourceLocation(Theatrical.MOD_ID, "block/fresnel/fresnel_hook_bar"); @@ -75,4 +92,9 @@ public float[] getTransforms(BlockState fixtureBlockState, BlockState supportBlo } return new float[]{0, 0.5F, 0}; } + + @Override + public List getDMXPersonalities() { + return PERSONALITIES; + } } diff --git a/common/src/main/java/dev/imabad/theatrical/fixtures/LEDPanelFixture.java b/common/src/main/java/dev/imabad/theatrical/fixtures/LEDPanelFixture.java index f979df0..751edbe 100644 --- a/common/src/main/java/dev/imabad/theatrical/fixtures/LEDPanelFixture.java +++ b/common/src/main/java/dev/imabad/theatrical/fixtures/LEDPanelFixture.java @@ -3,12 +3,23 @@ import dev.imabad.theatrical.Theatrical; import dev.imabad.theatrical.api.Fixture; import dev.imabad.theatrical.api.HangType; +import dev.imabad.theatrical.api.dmx.DMXPersonality; import dev.imabad.theatrical.blocks.rigging.PipeBlock; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.state.BlockState; +import java.util.Collections; +import java.util.List; + public class LEDPanelFixture extends Fixture { + private static final List PERSONALITIES = Collections.singletonList( + new DMXPersonality(4, "4-Channel Mode") + .addSlot(SharedSlots.INTENSITY) + .addSlot(SharedSlots.RED) + .addSlot(SharedSlots.GREEN) + .addSlot(SharedSlots.BLUE) + ); private static final ResourceLocation STATIC_MODEL = new ResourceLocation(Theatrical.MOD_ID, "block/led_panel"); @@ -75,4 +86,9 @@ public float[] getTransforms(BlockState fixtureBlockState, BlockState supportBlo return new float[]{0,0,0}; } + @Override + public List getDMXPersonalities() { + return PERSONALITIES; + } + } diff --git a/common/src/main/java/dev/imabad/theatrical/fixtures/MovingLightFixture.java b/common/src/main/java/dev/imabad/theatrical/fixtures/MovingLightFixture.java index 6bf71fa..4a9b42d 100644 --- a/common/src/main/java/dev/imabad/theatrical/fixtures/MovingLightFixture.java +++ b/common/src/main/java/dev/imabad/theatrical/fixtures/MovingLightFixture.java @@ -3,13 +3,28 @@ import dev.imabad.theatrical.Theatrical; import dev.imabad.theatrical.api.Fixture; import dev.imabad.theatrical.api.HangType; +import dev.imabad.theatrical.api.dmx.DMXPersonality; import dev.imabad.theatrical.blocks.light.BaseLightBlock; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.state.BlockState; +import java.util.Collections; +import java.util.List; + public class MovingLightFixture extends Fixture { + private static final List PERSONALITIES = Collections.singletonList( + new DMXPersonality(7, "7-Channel Mode") + .addSlot(SharedSlots.INTENSITY) + .addSlot(SharedSlots.RED) + .addSlot(SharedSlots.GREEN) + .addSlot(SharedSlots.BLUE) + .addSlot(SharedSlots.FOCUS) + .addSlot(SharedSlots.PAN) + .addSlot(SharedSlots.TILT) + ); + private static final ResourceLocation TILT_MODEL = new ResourceLocation(Theatrical.MOD_ID, "block/moving_light/moving_head_tilt"); private static final ResourceLocation PAN_MODEL = new ResourceLocation(Theatrical.MOD_ID, "block/moving_light/moving_head_pan"); private static final ResourceLocation STATIC_MODEL = new ResourceLocation(Theatrical.MOD_ID, "block/moving_light/moving_head_static"); @@ -76,4 +91,9 @@ public float[] getTransforms(BlockState fixtureBlockState, BlockState supportBlo } return new float[]{0, -0.35F, 0}; } + + @Override + public List getDMXPersonalities() { + return PERSONALITIES; + } } diff --git a/common/src/main/java/dev/imabad/theatrical/fixtures/RedstoneInterfaceFixture.java b/common/src/main/java/dev/imabad/theatrical/fixtures/RedstoneInterfaceFixture.java new file mode 100644 index 0000000..edb17ee --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/fixtures/RedstoneInterfaceFixture.java @@ -0,0 +1,79 @@ +package dev.imabad.theatrical.fixtures; + +import dev.imabad.theatrical.api.Fixture; +import dev.imabad.theatrical.api.HangType; +import dev.imabad.theatrical.api.dmx.DMXPersonality; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.Collections; +import java.util.List; + +// This isn't a real fixture, but it requires a fixture object now... +public class RedstoneInterfaceFixture extends Fixture { + + private static final List PERSONALITIES = Collections.singletonList( + new DMXPersonality(1, "1-Channel Mode") + .addSlot(SharedSlots.INTENSITY) + ); + + @Override + public ResourceLocation getTiltModel() { + return null; + } + + @Override + public ResourceLocation getPanModel() { + return null; + } + + @Override + public ResourceLocation getStaticModel() { + return null; + } + + @Override + public float[] getTiltRotationPosition() { + return new float[0]; + } + + @Override + public float[] getPanRotationPosition() { + return new float[0]; + } + + @Override + public float[] getBeamStartPosition() { + return new float[0]; + } + + @Override + public float getDefaultRotation() { + return 0; + } + + @Override + public float getBeamWidth() { + return 0; + } + + @Override + public float getRayTraceRotation() { + return 0; + } + + @Override + public HangType getHangType() { + return null; + } + + @Override + public float[] getTransforms(BlockState fixtureBlockState, BlockState supportBlockState) { + return new float[0]; + } + + @Override + public List getDMXPersonalities() { + return PERSONALITIES; + } +} diff --git a/common/src/main/java/dev/imabad/theatrical/fixtures/SharedSlots.java b/common/src/main/java/dev/imabad/theatrical/fixtures/SharedSlots.java new file mode 100644 index 0000000..017fff2 --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/fixtures/SharedSlots.java @@ -0,0 +1,17 @@ +package dev.imabad.theatrical.fixtures; + +import ch.bildspur.artnet.rdm.RDMSlotID; +import ch.bildspur.artnet.rdm.RDMSlotType; +import dev.imabad.theatrical.api.dmx.DMXSlot; + +public class SharedSlots { + + public static final DMXSlot INTENSITY = new DMXSlot("Intensity",RDMSlotType.ST_PRIMARY, RDMSlotID.SD_INTENSITY); + public static final DMXSlot RED = new DMXSlot("Red",RDMSlotType.ST_PRIMARY, RDMSlotID.SD_COLOR_SUB_CYAN); + public static final DMXSlot GREEN = new DMXSlot("Green",RDMSlotType.ST_PRIMARY, RDMSlotID.SD_COLOR_SUB_MAGENTA); + public static final DMXSlot BLUE = new DMXSlot("Blue",RDMSlotType.ST_PRIMARY, RDMSlotID.SD_COLOR_SUB_YELLOW); + public static final DMXSlot PAN = new DMXSlot("Pan",RDMSlotType.ST_PRIMARY, RDMSlotID.SD_PAN); + public static final DMXSlot TILT = new DMXSlot("Tilt",RDMSlotType.ST_PRIMARY, RDMSlotID.SD_TILT); + public static final DMXSlot FOCUS = new DMXSlot("Focus",RDMSlotType.ST_PRIMARY, RDMSlotID.SD_BEAM_SIZE_IRIS); + +} diff --git a/common/src/main/java/dev/imabad/theatrical/mixin/client/OptionsScreenMixin.java b/common/src/main/java/dev/imabad/theatrical/mixin/client/OptionsScreenMixin.java new file mode 100644 index 0000000..b96c20f --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/mixin/client/OptionsScreenMixin.java @@ -0,0 +1,34 @@ +package dev.imabad.theatrical.mixin.client; + +import dev.imabad.theatrical.client.gui.screen.ArtNetConfigurationScreen; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.layouts.GridLayout; +import net.minecraft.client.gui.screens.OptionsScreen; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.spongepowered.asm.mixin.Debug; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.function.Supplier; + +@Mixin(OptionsScreen.class) +@Debug(export = true) +public abstract class OptionsScreenMixin extends Screen { + + protected OptionsScreenMixin(Component title) { + super(title); + } + + @Shadow protected abstract Button openScreenButton(Component message, Supplier screenSupplier); + + @Inject(method = "init()V", at = @At(value = "INVOKE", target="Lnet/minecraft/client/gui/layouts/GridLayout$RowHelper;addChild(Lnet/minecraft/client/gui/layouts/LayoutElement;ILnet/minecraft/client/gui/layouts/LayoutSettings;)Lnet/minecraft/client/gui/layouts/LayoutElement;"), locals = LocalCapture.CAPTURE_FAILHARD) + private void onCreatePauseMenu(CallbackInfo ci, GridLayout gridLayout, GridLayout.RowHelper rowHelper){ + rowHelper.addChild(this.openScreenButton(Component.translatable("button.artnetconfig"), () -> new ArtNetConfigurationScreen((OptionsScreen)(Object) this))); + } + +} diff --git a/common/src/main/java/dev/imabad/theatrical/net/SendArtNetData.java b/common/src/main/java/dev/imabad/theatrical/net/SendArtNetData.java deleted file mode 100644 index e6518ff..0000000 --- a/common/src/main/java/dev/imabad/theatrical/net/SendArtNetData.java +++ /dev/null @@ -1,56 +0,0 @@ -package dev.imabad.theatrical.net; - -import dev.architectury.networking.NetworkManager; -import dev.architectury.networking.simple.BaseC2SMessage; -import dev.architectury.networking.simple.MessageType; -import dev.imabad.theatrical.Theatrical; -import dev.imabad.theatrical.blockentities.interfaces.ArtNetInterfaceBlockEntity; -import net.minecraft.core.BlockPos; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; - -public class SendArtNetData extends BaseC2SMessage { - private BlockPos pos; - private byte[] artNetData; - - public SendArtNetData(BlockPos blockPos, byte[] data){ - pos = blockPos; - artNetData = data; - } - - SendArtNetData(FriendlyByteBuf buf){ - pos = buf.readBlockPos(); - artNetData = buf.readByteArray(); - } - - @Override - public MessageType getType() { - return TheatricalNet.SEND_ARTNET_TO_SERVER; - } - - @Override - public void write(FriendlyByteBuf buf) { - buf.writeBlockPos(pos); - buf.writeByteArray(artNetData); - } - - @Override - public void handle(NetworkManager.PacketContext context) { - Level level = context.getPlayer().level(); - BlockEntity be = level.getBlockEntity(pos); - if(be instanceof ArtNetInterfaceBlockEntity artnetInterface) { - if(level.getServer() != null ){ - if(artnetInterface.getOwnerUUID().equals(context.getPlayer().getUUID())){ - if(context.getPlayer().hasPermissions(level.getServer().getOperatorUserPermissionLevel())){ - artnetInterface.update(artNetData); - } else { - Theatrical.LOGGER.info("{} tried to send ArtNet data but is not authorized!", context.getPlayer().getName()); - } - } else { - Theatrical.LOGGER.info("{} tried to send ArtNet data using a block they do not own!", context.getPlayer().getName()); - } - } - } - } -} diff --git a/common/src/main/java/dev/imabad/theatrical/net/TheatricalNet.java b/common/src/main/java/dev/imabad/theatrical/net/TheatricalNet.java index bb88337..1a15dd4 100644 --- a/common/src/main/java/dev/imabad/theatrical/net/TheatricalNet.java +++ b/common/src/main/java/dev/imabad/theatrical/net/TheatricalNet.java @@ -3,13 +3,20 @@ import dev.architectury.networking.simple.MessageType; import dev.architectury.networking.simple.SimpleNetworkManager; import dev.imabad.theatrical.Theatrical; +import dev.imabad.theatrical.net.artnet.*; public interface TheatricalNet { SimpleNetworkManager MAIN = SimpleNetworkManager.create(Theatrical.MOD_ID); - + // C2S MessageType SEND_ARTNET_TO_SERVER = MAIN.registerC2S("send_artnet_to_server", SendArtNetData::new); MessageType UPDATE_ARTNET_INTERFACE = MAIN.registerC2S("update_artnet_interface", UpdateArtNetInterface::new); MessageType UPDATE_DMX_FIXTURE = MAIN.registerC2S("update_dmx_fixture", UpdateDMXFixture::new); MessageType UPDATE_FIXTURE_POS = MAIN.registerC2S("update_fixture_pos", UpdateFixturePosition::new); + MessageType RDM_UPDATE_FIXTURE = MAIN.registerC2S("rdm_update_fixture", RDMUpdateConsumer::new); + MessageType REQUEST_CONSUMERS = MAIN.registerC2S("request_consumers", RequestConsumers::new); + + // S2C + MessageType NOTIFY_CONSUMER_CHANGE = MAIN.registerS2C("notify_consumer_change", NotifyConsumerChange::new); + MessageType LIST_CONSUMERS = MAIN.registerS2C("list_consumers", ListConsumers::new); static void init(){} } diff --git a/common/src/main/java/dev/imabad/theatrical/net/UpdateDMXFixture.java b/common/src/main/java/dev/imabad/theatrical/net/UpdateDMXFixture.java index 8948c22..35906d9 100644 --- a/common/src/main/java/dev/imabad/theatrical/net/UpdateDMXFixture.java +++ b/common/src/main/java/dev/imabad/theatrical/net/UpdateDMXFixture.java @@ -12,16 +12,18 @@ public class UpdateDMXFixture extends BaseC2SMessage { private BlockPos pos; - private int dmxAddress; + private int dmxAddress, dmxUniverse; - public UpdateDMXFixture(BlockPos blockPos, int dmxAddress){ + public UpdateDMXFixture(BlockPos blockPos, int dmxAddress, int dmxUniverse){ this.pos = blockPos; this.dmxAddress = dmxAddress; + this.dmxUniverse = dmxUniverse; } UpdateDMXFixture(FriendlyByteBuf buf){ pos = buf.readBlockPos(); dmxAddress = buf.readInt(); + dmxUniverse = buf.readInt(); } @Override @@ -33,6 +35,7 @@ public MessageType getType() { public void write(FriendlyByteBuf buf) { buf.writeBlockPos(pos); buf.writeInt(dmxAddress); + buf.writeInt(dmxUniverse); } @Override @@ -40,8 +43,10 @@ public void handle(NetworkManager.PacketContext context) { BlockEntity be = context.getPlayer().level().getBlockEntity(pos); if(be instanceof BaseDMXConsumerLightBlockEntity dmxConsumerLightBlock){ dmxConsumerLightBlock.setChannelStartPoint(dmxAddress); + dmxConsumerLightBlock.setUniverse(dmxUniverse); } else if(be instanceof RedstoneInterfaceBlockEntity redstoneInterfaceBlockEntity){ redstoneInterfaceBlockEntity.setChannelStartPoint(dmxAddress); + redstoneInterfaceBlockEntity.setUniverse(dmxUniverse); } } } diff --git a/common/src/main/java/dev/imabad/theatrical/net/artnet/ListConsumers.java b/common/src/main/java/dev/imabad/theatrical/net/artnet/ListConsumers.java new file mode 100644 index 0000000..349994e --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/net/artnet/ListConsumers.java @@ -0,0 +1,71 @@ +package dev.imabad.theatrical.net.artnet; + +import ch.bildspur.artnet.rdm.RDMDeviceId; +import dev.architectury.networking.NetworkManager; +import dev.architectury.networking.simple.BaseS2CMessage; +import dev.architectury.networking.simple.MessageType; +import dev.imabad.theatrical.TheatricalClient; +import dev.imabad.theatrical.dmx.DMXDevice; +import dev.imabad.theatrical.net.TheatricalNet; +import net.fabricmc.api.EnvType; +import net.minecraft.network.FriendlyByteBuf; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +public class ListConsumers extends BaseS2CMessage { + + private int universe; + private List dmxDevices; + + public ListConsumers(int universe, List dmxDevices){ + this.universe = universe; + this.dmxDevices = dmxDevices; + } + + public ListConsumers(FriendlyByteBuf buf){ + universe = buf.readInt(); + int count = buf.readInt(); + dmxDevices = new ArrayList<>(); + for(int i = 0; i < count; i++){ + dmxDevices.add(new DMXDevice(new RDMDeviceId(buf.readByteArray(6)), buf.readInt(), buf.readInt(), + buf.readInt(), buf.readInt(), buf.readUtf(), buf.readResourceLocation())); + } + } + + @Override + public MessageType getType() { + return TheatricalNet.LIST_CONSUMERS; + } + + @Override + public void write(FriendlyByteBuf buf) { + buf.writeInt(universe); + buf.writeInt(dmxDevices.size()); + for (DMXDevice dmxDevice : dmxDevices) { + buf.writeByteArray(dmxDevice.getDeviceId().toBytes()); + buf.writeInt(dmxDevice.getDmxStartAddress()); + buf.writeInt(dmxDevice.getDmxChannelCount()); + buf.writeInt(dmxDevice.getDeviceTypeId()); + buf.writeInt(dmxDevice.getActivePersonality()); + buf.writeUtf(dmxDevice.getModelName()); + buf.writeResourceLocation(dmxDevice.getFixtureID()); + } + } + + @Override + public void handle(NetworkManager.PacketContext context) { + if(context.getEnv() == EnvType.CLIENT){ + TheatricalClient.handleListConsumers(this); + } + } + + public int getUniverse() { + return universe; + } + + public List getDmxDevices() { + return dmxDevices; + } +} diff --git a/common/src/main/java/dev/imabad/theatrical/net/artnet/NotifyConsumerChange.java b/common/src/main/java/dev/imabad/theatrical/net/artnet/NotifyConsumerChange.java new file mode 100644 index 0000000..40c2f24 --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/net/artnet/NotifyConsumerChange.java @@ -0,0 +1,80 @@ +package dev.imabad.theatrical.net.artnet; + +import ch.bildspur.artnet.rdm.RDMDeviceId; +import dev.architectury.networking.NetworkManager; +import dev.architectury.networking.simple.BaseS2CMessage; +import dev.architectury.networking.simple.MessageType; +import dev.imabad.theatrical.TheatricalClient; +import dev.imabad.theatrical.dmx.DMXDevice; +import dev.imabad.theatrical.net.TheatricalNet; +import net.fabricmc.api.EnvType; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; + +public class NotifyConsumerChange extends BaseS2CMessage { + + public enum ChangeType{ + ADD, + UPDATE, + REMOVE; + } + + private int universe; + private ChangeType changeType; + private DMXDevice dmxDevice; + + public NotifyConsumerChange(int universe, ChangeType changeType, DMXDevice dmxDevice){ + this.universe = universe; + this.changeType = changeType; + this.dmxDevice = dmxDevice; + } + + public NotifyConsumerChange(FriendlyByteBuf buf){ + universe = buf.readInt(); + changeType = ChangeType.valueOf(buf.readUtf()); + if(buf.readBoolean()) { + dmxDevice = new DMXDevice(new RDMDeviceId(buf.readByteArray(6)), buf.readInt(), + buf.readInt(), buf.readInt(), buf.readInt(), buf.readUtf(), buf.readResourceLocation()); + } + } + + @Override + public MessageType getType() { + return TheatricalNet.NOTIFY_CONSUMER_CHANGE; + } + + @Override + public void write(FriendlyByteBuf buf) { + buf.writeInt(universe); + buf.writeUtf(changeType.name()); + buf.writeBoolean(dmxDevice != null); + if(dmxDevice != null) { + buf.writeByteArray(dmxDevice.getDeviceId().toBytes()); + buf.writeInt(dmxDevice.getDmxStartAddress()); + buf.writeInt(dmxDevice.getDmxChannelCount()); + buf.writeInt(dmxDevice.getDeviceTypeId()); + buf.writeInt(dmxDevice.getActivePersonality()); + buf.writeUtf(dmxDevice.getModelName()); + buf.writeResourceLocation(dmxDevice.getFixtureID()); + } + } + + @Override + public void handle(NetworkManager.PacketContext context) { + if(context.getEnv() == EnvType.CLIENT){ + TheatricalClient.handleConsumerChange(this); + } + } + + public int getUniverse() { + return universe; + } + + public ChangeType getChangeType() { + return changeType; + } + + public DMXDevice getDmxDevice() { + return dmxDevice; + } +} diff --git a/common/src/main/java/dev/imabad/theatrical/net/artnet/RDMUpdateConsumer.java b/common/src/main/java/dev/imabad/theatrical/net/artnet/RDMUpdateConsumer.java new file mode 100644 index 0000000..643ea58 --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/net/artnet/RDMUpdateConsumer.java @@ -0,0 +1,75 @@ +package dev.imabad.theatrical.net.artnet; + +import ch.bildspur.artnet.rdm.RDMDeviceId; +import dev.architectury.networking.NetworkManager; +import dev.architectury.networking.simple.BaseC2SMessage; +import dev.architectury.networking.simple.MessageType; +import dev.imabad.theatrical.Theatrical; +import dev.imabad.theatrical.blockentities.interfaces.RedstoneInterfaceBlockEntity; +import dev.imabad.theatrical.blockentities.light.BaseDMXConsumerLightBlockEntity; +import dev.imabad.theatrical.dmx.DMXDevice; +import dev.imabad.theatrical.dmx.DMXNetworkData; +import dev.imabad.theatrical.net.TheatricalNet; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + +public class RDMUpdateConsumer extends BaseC2SMessage { + + private int universe, newAddress;; + private RDMDeviceId dmxDevice; + + public RDMUpdateConsumer(int universe, RDMDeviceId dmxDevice, int newAddress){ + this.universe = universe; + this.dmxDevice = dmxDevice; + this.newAddress = newAddress; + } + + public RDMUpdateConsumer(FriendlyByteBuf buf){ + universe = buf.readInt(); + dmxDevice = new RDMDeviceId(buf.readByteArray(6)); + newAddress = buf.readInt(); + } + + @Override + public MessageType getType() { + return TheatricalNet.RDM_UPDATE_FIXTURE; + } + + @Override + public void write(FriendlyByteBuf buf) { + buf.writeInt(universe); + buf.writeByteArray(dmxDevice.toBytes()); + buf.writeInt(newAddress); + } + + @Override + public void handle(NetworkManager.PacketContext context) { + Level level = context.getPlayer().level(); + if(level.getServer() != null ) { + if (context.getPlayer().hasPermissions(level.getServer().getOperatorUserPermissionLevel())) { + BlockPos consumerPos = DMXNetworkData.getInstance().getConsumerPos(universe, dmxDevice); + if(consumerPos != null){ + BlockEntity be = context.getPlayer().level().getBlockEntity(consumerPos); + if(be instanceof BaseDMXConsumerLightBlockEntity dmxConsumerLightBlock){ + dmxConsumerLightBlock.setChannelStartPoint(newAddress); + } else if(be instanceof RedstoneInterfaceBlockEntity redstoneInterfaceBlockEntity){ + redstoneInterfaceBlockEntity.setChannelStartPoint(newAddress); + } + } + } else { + Theatrical.LOGGER.info("{} tried to send ArtNet data but is not authorized!", context.getPlayer().getName()); + } + } + } + + public int getUniverse() { + return universe; + } + + public int getNewAddress() { + return newAddress; + } +} diff --git a/common/src/main/java/dev/imabad/theatrical/net/artnet/RequestConsumers.java b/common/src/main/java/dev/imabad/theatrical/net/artnet/RequestConsumers.java new file mode 100644 index 0000000..37d1320 --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/net/artnet/RequestConsumers.java @@ -0,0 +1,64 @@ +package dev.imabad.theatrical.net.artnet; + +import dev.architectury.networking.NetworkManager; +import dev.architectury.networking.simple.BaseC2SMessage; +import dev.architectury.networking.simple.MessageType; +import dev.imabad.theatrical.Theatrical; +import dev.imabad.theatrical.api.dmx.DMXConsumer; +import dev.imabad.theatrical.dmx.DMXDevice; +import dev.imabad.theatrical.dmx.DMXNetworkData; +import dev.imabad.theatrical.net.TheatricalNet; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.Level; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class RequestConsumers extends BaseC2SMessage { + + private int universe; + + public RequestConsumers(int universe){ + this.universe = universe; + } + + public RequestConsumers(FriendlyByteBuf byteBuf){ + universe = byteBuf.readInt(); + } + + @Override + public MessageType getType() { + return TheatricalNet.REQUEST_CONSUMERS; + } + + @Override + public void write(FriendlyByteBuf buf) { + buf.writeInt(universe); + } + + @Override + public void handle(NetworkManager.PacketContext context) { + Level level = context.getPlayer().level(); + if(level.getServer() != null ) { + if (context.getPlayer().hasPermissions(level.getServer().getOperatorUserPermissionLevel())) { + if(DMXNetworkData.getInstance().isKnownSender((ServerPlayer) context.getPlayer())){ + List devices = new ArrayList<>(); + Collection consumers = DMXNetworkData.getInstance().getConsumers(universe); + if(consumers == null){ + return; + } + consumers.forEach(consumer -> { + devices.add(new DMXDevice(consumer.getDeviceId(), consumer.getChannelStart(), + consumer.getChannelCount(), consumer.getDeviceTypeId(), consumer.getActivePersonality(), consumer.getModelName(), + consumer.getFixtureId())); + }); + new ListConsumers(universe, devices).sendTo((ServerPlayer)context.getPlayer()); + } + } else { + Theatrical.LOGGER.info("{} tried to send ArtNet data but is not authorized!", context.getPlayer().getName()); + } + } + } +} diff --git a/common/src/main/java/dev/imabad/theatrical/net/artnet/SendArtNetData.java b/common/src/main/java/dev/imabad/theatrical/net/artnet/SendArtNetData.java new file mode 100644 index 0000000..5b91bda --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/net/artnet/SendArtNetData.java @@ -0,0 +1,55 @@ +package dev.imabad.theatrical.net.artnet; + +import dev.architectury.networking.NetworkManager; +import dev.architectury.networking.simple.BaseC2SMessage; +import dev.architectury.networking.simple.MessageType; +import dev.imabad.theatrical.Theatrical; +import dev.imabad.theatrical.blockentities.interfaces.ArtNetInterfaceBlockEntity; +import dev.imabad.theatrical.dmx.DMXNetworkData; +import dev.imabad.theatrical.net.TheatricalNet; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + +public class SendArtNetData extends BaseC2SMessage { + private int universe; + private byte[] artNetData; + + public SendArtNetData(int universe, byte[] data){ + this.universe = universe; + artNetData = data; + } + + public SendArtNetData(FriendlyByteBuf buf){ + universe = buf.readInt(); + artNetData = buf.readByteArray(); + } + + @Override + public MessageType getType() { + return TheatricalNet.SEND_ARTNET_TO_SERVER; + } + + @Override + public void write(FriendlyByteBuf buf) { + buf.writeInt(universe); + buf.writeByteArray(artNetData); + } + + @Override + public void handle(NetworkManager.PacketContext context) { + Level level = context.getPlayer().level(); + if(level.getServer() != null ) { + if (context.getPlayer().hasPermissions(level.getServer().getOperatorUserPermissionLevel())) { + DMXNetworkData.getInstance().getConsumers(universe).forEach(consumer -> { + consumer.consume(artNetData); + }); + DMXNetworkData.getInstance().addKnownSender((ServerPlayer) context.getPlayer()); + } else { + Theatrical.LOGGER.info("{} tried to send ArtNet data but is not authorized!", context.getPlayer().getName()); + } + } + } +} diff --git a/common/src/main/java/dev/imabad/theatrical/protocols/artnet/ArtNetManager.java b/common/src/main/java/dev/imabad/theatrical/protocols/artnet/ArtNetManager.java index dcebde1..f1349e8 100644 --- a/common/src/main/java/dev/imabad/theatrical/protocols/artnet/ArtNetManager.java +++ b/common/src/main/java/dev/imabad/theatrical/protocols/artnet/ArtNetManager.java @@ -1,31 +1,42 @@ package dev.imabad.theatrical.protocols.artnet; import ch.bildspur.artnet.ArtNetClient; +import dev.imabad.theatrical.TheatricalClient; +import dev.imabad.theatrical.config.TheatricalConfig; import dev.imabad.theatrical.dmx.TheatricalArtNetClient; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.HashMap; public class ArtNetManager { - private final HashMap clients = new HashMap<>(); + private TheatricalArtNetClient artNetClient; - public TheatricalArtNetClient getClient(String ip){ - if(!this.clients.containsKey(ip)){ - return this.newClient(ip); + public TheatricalArtNetClient getClient(){ + if(this.artNetClient == null){ + this.artNetClient = newClient(); } - return this.clients.get(ip); + return this.artNetClient; } - private TheatricalArtNetClient newClient(String ip){ - TheatricalArtNetClient client = new TheatricalArtNetClient(); - clients.put(ip, client); - client.start(ip); - return client; + private TheatricalArtNetClient newClient(){ + try { + InetAddress byName = InetAddress.getByName(TheatricalConfig.INSTANCE.CLIENT.artNetIP); + TheatricalArtNetClient client = new TheatricalArtNetClient(byName); + client.start(byName); + return client; + } catch (UnknownHostException var3) { + var3.printStackTrace(); + } + return null; } public void shutdownAll(){ - clients.values().forEach(ArtNetClient::stop); - clients.clear(); + if(artNetClient == null){ + return; + } + artNetClient.stop(); + artNetClient = null; } - } diff --git a/common/src/main/java/dev/imabad/theatrical/util/ByteUtils.java b/common/src/main/java/dev/imabad/theatrical/util/ByteUtils.java new file mode 100644 index 0000000..4ad406b --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/util/ByteUtils.java @@ -0,0 +1,19 @@ +package dev.imabad.theatrical.util; + +import java.nio.ByteBuffer; + +public class ByteUtils { + private static ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); + + public static byte[] longToBytes(long x) { + buffer.putLong(0, x); + return buffer.array(); + } + public static short calculateChecksum(byte[] data){ + int checksum = 0; + for (byte b : data) { + checksum += (b & 0xFF); // ensure b is treated as an unsigned byte + } + return (short) (checksum & 0xFFFF); // ensure the checksum is 16-bit + } +} diff --git a/common/src/main/java/dev/imabad/theatrical/util/RndUtils.java b/common/src/main/java/dev/imabad/theatrical/util/RndUtils.java new file mode 100644 index 0000000..0dda3ba --- /dev/null +++ b/common/src/main/java/dev/imabad/theatrical/util/RndUtils.java @@ -0,0 +1,15 @@ +package dev.imabad.theatrical.util; + +import net.minecraft.util.RandomSource; + +public class RndUtils { + + public static void nextBytes(RandomSource randomSource, byte[] bytes){ + for (int i = 0, len = bytes.length; i < len; ) + for (int rnd = randomSource.nextInt(), + n = Math.min(len - i, Integer.SIZE/Byte.SIZE); + n-- > 0; rnd >>= Byte.SIZE) + bytes[i++] = (byte)rnd; + } + +} diff --git a/common/src/main/resources/theatrical-common.mixins.json b/common/src/main/resources/theatrical-common.mixins.json index f338e7e..24ede0b 100644 --- a/common/src/main/resources/theatrical-common.mixins.json +++ b/common/src/main/resources/theatrical-common.mixins.json @@ -4,13 +4,15 @@ "package": "dev.imabad.theatrical.mixin", "compatibilityLevel": "JAVA_17", "injectors": { - "defaultRequire": 1 + "defaultRequire": 1, + "maxShiftBy": 5 }, "mixins": [ "ClipContextAccessor" ], "client": [ "client.ClientMixin", - "client.LevelRendererMixin" + "client.LevelRendererMixin", + "client.OptionsScreenMixin" ] } diff --git a/fabric/src/main/java/dev/imabad/theatrical/fabric/TheatricalClientFabric.java b/fabric/src/main/java/dev/imabad/theatrical/fabric/TheatricalClientFabric.java index 7d95cd7..431f476 100644 --- a/fabric/src/main/java/dev/imabad/theatrical/fabric/TheatricalClientFabric.java +++ b/fabric/src/main/java/dev/imabad/theatrical/fabric/TheatricalClientFabric.java @@ -6,8 +6,11 @@ import dev.imabad.theatrical.fixtures.Fixtures; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientPacketListener; public class TheatricalClientFabric implements ClientModInitializer { @Override @@ -24,12 +27,13 @@ public void onInitializeClient() { } } }); + WorldRenderEvents.START.register(this::renderWorldStartFabric); if(Platform.isDevelopmentEnvironment()) { - WorldRenderEvents.START.register(this::renderWorldStartFabric); WorldRenderEvents.AFTER_TRANSLUCENT.register(this::renderWorldLastFabric); } } + private void renderWorldStartFabric(WorldRenderContext context) { TheatricalClient.renderWorldLastAfterTripwire(context.worldRenderer()); } diff --git a/fabric/src/main/java/dev/imabad/theatrical/fabric/TheatricalExpectPlatformImpl.java b/fabric/src/main/java/dev/imabad/theatrical/fabric/TheatricalExpectPlatformImpl.java index f267dc1..955ae0a 100644 --- a/fabric/src/main/java/dev/imabad/theatrical/fabric/TheatricalExpectPlatformImpl.java +++ b/fabric/src/main/java/dev/imabad/theatrical/fabric/TheatricalExpectPlatformImpl.java @@ -1,8 +1,10 @@ package dev.imabad.theatrical.fabric; +import dev.imabad.theatrical.Theatrical; import dev.imabad.theatrical.TheatricalExpectPlatform; import net.fabricmc.fabric.api.client.model.BakedModelManagerHelper; import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.resources.ResourceLocation; @@ -10,6 +12,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType; import java.nio.file.Path; +import java.util.Optional; public class TheatricalExpectPlatformImpl { /** @@ -22,5 +25,11 @@ public static Path getConfigDirectory() { public static BakedModel getBakedModel(ResourceLocation modelLocation){ return BakedModelManagerHelper.getModel(Minecraft.getInstance().getModelManager(), modelLocation); } - + public static String getModVersion() { + Optional modContainer = FabricLoader.getInstance().getModContainer(Theatrical.MOD_ID); + if(modContainer.isPresent()){ + return modContainer.get().getMetadata().getVersion().getFriendlyString(); + } + return "Unknown"; + } } diff --git a/forge/src/main/java/dev/imabad/theatrical/forge/TheatricalExpectPlatformImpl.java b/forge/src/main/java/dev/imabad/theatrical/forge/TheatricalExpectPlatformImpl.java index 247c00d..01fc674 100644 --- a/forge/src/main/java/dev/imabad/theatrical/forge/TheatricalExpectPlatformImpl.java +++ b/forge/src/main/java/dev/imabad/theatrical/forge/TheatricalExpectPlatformImpl.java @@ -1,12 +1,20 @@ package dev.imabad.theatrical.forge; +import dev.imabad.theatrical.Theatrical; import dev.imabad.theatrical.TheatricalExpectPlatform; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.fml.ModContainer; +import net.minecraftforge.fml.ModLoader; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.loading.FMLPaths; +import net.minecraftforge.fml.loading.LoadingModList; +import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; import java.nio.file.Path; +import java.util.Optional; public class TheatricalExpectPlatformImpl { /** @@ -20,4 +28,12 @@ public static BakedModel getBakedModel(ResourceLocation modelLocation){ return Minecraft.getInstance().getModelManager().getModel(modelLocation); } + public static String getModVersion() { + ModFileInfo modFileById = LoadingModList.get().getModFileById(Theatrical.MOD_ID); + if(modFileById != null) { + return modFileById.versionString(); + } + return "Unknown"; + } + } diff --git a/gradle.properties b/gradle.properties index 73d53ad..0693767 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ neoforge_version=20.2.75-beta parchment_version=2023.12.10 -artnet4j_version=0.6.2 +artnet4j_version=0.6.3 snakeyaml_version=2.2 release_type=ALPHA diff --git a/neoforge/src/main/java/dev/imabad/theatrical/neoforge/DataEvent.java b/neoforge/src/main/java/dev/imabad/theatrical/neoforge/DataEvent.java index ddf8c98..bc9a432 100644 --- a/neoforge/src/main/java/dev/imabad/theatrical/neoforge/DataEvent.java +++ b/neoforge/src/main/java/dev/imabad/theatrical/neoforge/DataEvent.java @@ -96,6 +96,8 @@ protected void addTranslations() { add("fixture.pan", "Pan"); add("fixture.tilt", "Tilt"); add("screen.movinglight", "Moving Light"); + add("button.artnetconfig", "ArtNet Config"); + add("screen.artnetconfig.enabled", "ArtNet Enabled: %s"); } } diff --git a/neoforge/src/main/java/dev/imabad/theatrical/neoforge/TheatricalExpectPlatformImpl.java b/neoforge/src/main/java/dev/imabad/theatrical/neoforge/TheatricalExpectPlatformImpl.java index d616ba3..62a13de 100644 --- a/neoforge/src/main/java/dev/imabad/theatrical/neoforge/TheatricalExpectPlatformImpl.java +++ b/neoforge/src/main/java/dev/imabad/theatrical/neoforge/TheatricalExpectPlatformImpl.java @@ -1,10 +1,13 @@ package dev.imabad.theatrical.neoforge; +import dev.imabad.theatrical.Theatrical; import dev.imabad.theatrical.TheatricalExpectPlatform; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.resources.ResourceLocation; import net.neoforged.fml.loading.FMLPaths; +import net.neoforged.fml.loading.LoadingModList; +import net.neoforged.fml.loading.moddiscovery.ModFileInfo; import java.nio.file.Path; @@ -19,5 +22,11 @@ public static Path getConfigDirectory() { public static BakedModel getBakedModel(ResourceLocation modelLocation){ return Minecraft.getInstance().getModelManager().getModel(modelLocation); } - + public static String getModVersion() { + ModFileInfo modFileById = LoadingModList.get().getModFileById(Theatrical.MOD_ID); + if(modFileById != null) { + return modFileById.versionString(); + } + return "Unknown"; + } }