1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-06-26 07:03:22 +00:00

Move all event handlers to a common class

While this does now involve a little more indirection, by having a
single location for all hooks it's much easier to keep event listeners
consistent between loaders.

The diff is pretty noisy (I've inlined some classes, and ClientRegistry
got a big restructure), but functionality should be the same.
This commit is contained in:
Jonathan Coates 2022-11-08 10:46:09 +00:00
parent e8f9cdd221
commit 0908acbe9b
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
29 changed files with 646 additions and 494 deletions

View File

@ -24,6 +24,7 @@
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.config.ModConfigEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.registries.NewRegistryEvent;
import net.minecraftforge.registries.RegistryBuilder;
@ -113,4 +114,13 @@ public static void init(FMLCommonSetupEvent event) {
ForgeDetailRegistries.FLUID_STACK.addProvider(FluidData::fill);
}
@SubscribeEvent
public static void sync(ModConfigEvent.Loading event) {
Config.sync(event.getConfig());
}
@SubscribeEvent
public static void sync(ModConfigEvent.Reloading event) {
Config.sync(event.getConfig());
}
}

View File

@ -5,33 +5,158 @@
*/
package dan200.computercraft.client;
import dan200.computercraft.ComputerCraft;
import com.mojang.blaze3d.audio.Channel;
import com.mojang.blaze3d.vertex.PoseStack;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.client.pocket.ClientPocketComputers;
import dan200.computercraft.client.render.CableHighlightRenderer;
import dan200.computercraft.client.render.ItemPocketRenderer;
import dan200.computercraft.client.render.ItemPrintoutRenderer;
import dan200.computercraft.client.render.MonitorHighlightRenderer;
import dan200.computercraft.client.sound.SpeakerManager;
import dan200.computercraft.shared.CommonHooks;
import dan200.computercraft.shared.command.CommandComputerCraft;
import dan200.computercraft.shared.computer.core.ServerContext;
import dan200.computercraft.shared.media.items.ItemPrintout;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ClientPlayerNetworkEvent;
import net.minecraftforge.event.level.LevelEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.util.PauseAwareTimer;
import net.minecraft.Util;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.sounds.AudioStream;
import net.minecraft.client.sounds.SoundEngine;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.decoration.ItemFrame;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT)
public class ClientHooks {
@SubscribeEvent
public static void onWorldUnload(LevelEvent.Unload event) {
if (event.getLevel().isClientSide()) {
ClientMonitor.destroyAll();
SpeakerManager.reset();
import java.io.File;
import java.util.function.Consumer;
/**
* Event listeners for client-only code.
* <p>
* This is the client-only version of {@link CommonHooks}, and so should be where all client-specific event handlers are
* defined.
*/
public final class ClientHooks {
private ClientHooks() {
}
public static void onTick() {
FrameInfo.onTick();
}
public static void onRenderTick() {
PauseAwareTimer.tick(Minecraft.getInstance().isPaused());
FrameInfo.onRenderTick();
}
public static void onWorldUnload() {
ClientMonitor.destroyAll();
SpeakerManager.reset();
ClientPocketComputers.reset();
}
public static boolean onChatMessage(String message) {
return handleOpenComputerCommand(message);
}
public static boolean drawHighlight(PoseStack transform, MultiBufferSource bufferSource, Camera camera, BlockHitResult hit) {
return CableHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit)
|| MonitorHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit);
}
public static boolean onRenderHeldItem(
PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand,
float pitch, float equipProgress, float swingProgress, ItemStack stack
) {
if (stack.getItem() instanceof ItemPocketComputer) {
ItemPocketRenderer.INSTANCE.renderItemFirstPerson(transform, render, lightTexture, hand, pitch, equipProgress, swingProgress, stack);
return true;
}
if (stack.getItem() instanceof ItemPrintout) {
ItemPrintoutRenderer.INSTANCE.renderItemFirstPerson(transform, render, lightTexture, hand, pitch, equipProgress, swingProgress, stack);
return true;
}
return false;
}
public static boolean onRenderItemFrame(PoseStack transform, MultiBufferSource render, ItemFrame frame, ItemStack stack, int light) {
if (stack.getItem() instanceof ItemPrintout) {
ItemPrintoutRenderer.onRenderInFrame(transform, render, frame, stack, light);
return true;
}
return false;
}
public static void onPlayStreaming(SoundEngine engine, Channel channel, AudioStream stream) {
SpeakerManager.onPlayStreaming(engine, channel, stream);
}
/**
* Handle the {@link CommandComputerCraft#OPEN_COMPUTER} "clientside command". This isn't a true command, as we
* don't want it to actually be visible to the user.
*
* @param message The current chat message.
* @return Whether to cancel sending this message.
*/
private static boolean handleOpenComputerCommand(String message) {
if (!message.startsWith(CommandComputerCraft.OPEN_COMPUTER)) return false;
var server = Minecraft.getInstance().getSingleplayerServer();
if (server == null) return false;
var idStr = message.substring(CommandComputerCraft.OPEN_COMPUTER.length()).trim();
int id;
try {
id = Integer.parseInt(idStr);
} catch (NumberFormatException ignore) {
return false;
}
var file = new File(ServerContext.get(server).storageDir().toFile(), "computer/" + id);
if (!file.isDirectory()) return false;
Util.getPlatform().openFile(file);
return true;
}
/**
* Add additional information about the currently targeted block to the debug screen.
*
* @param addText A callback which adds a single line of text.
*/
public static void addDebugInfo(Consumer<String> addText) {
var minecraft = Minecraft.getInstance();
if (!minecraft.options.renderDebug || minecraft.level == null) return;
if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return;
var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos());
if (tile instanceof TileMonitor monitor) {
addText.accept("");
addText.accept(
String.format("Targeted monitor: (%d, %d), %d x %d", monitor.getXIndex(), monitor.getYIndex(), monitor.getWidth(), monitor.getHeight())
);
} else if (tile instanceof TileTurtle turtle) {
addText.accept("");
addText.accept("Targeted turtle:");
addText.accept(String.format("Id: %d", turtle.getComputerID()));
addTurtleUpgrade(addText, turtle, TurtleSide.LEFT);
addTurtleUpgrade(addText, turtle, TurtleSide.RIGHT);
}
}
@SubscribeEvent
public static void onLogIn(ClientPlayerNetworkEvent.LoggingIn event) {
ClientPocketComputers.reset();
}
@SubscribeEvent
public static void onLogOut(ClientPlayerNetworkEvent.LoggingOut event) {
ClientPocketComputers.reset();
private static void addTurtleUpgrade(Consumer<String> out, TileTurtle turtle, TurtleSide side) {
var upgrade = turtle.getUpgrade(side);
if (upgrade != null) out.accept(String.format("Upgrade[%s]: %s", side, upgrade.getUpgradeID()));
}
}

View File

@ -10,9 +10,9 @@
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.pocket.ClientPocketComputers;
import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
import dan200.computercraft.client.render.TurtleModelLoader;
import dan200.computercraft.client.turtle.TurtleModemModeller;
import dan200.computercraft.core.util.Colour;
import dan200.computercraft.shared.ModRegistry;
@ -21,26 +21,86 @@
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.media.items.ItemDisk;
import dan200.computercraft.shared.media.items.ItemTreasureDisk;
import net.minecraft.client.color.item.ItemColor;
import net.minecraft.client.gui.screens.MenuScreens;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.client.renderer.item.ItemPropertyFunction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.item.Item;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ModelEvent;
import net.minecraftforge.client.event.RegisterColorHandlersEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import java.io.IOException;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* Registers textures and models for items.
* Registers client-side objects, such as {@link BlockEntityRendererProvider}s and
* {@link MenuScreens.ScreenConstructor}.
* <p>
* The functions in this class should be called from a loader-specific class.
*
* @see ModRegistry The common registry for actual game objects.
*/
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
public final class ClientRegistry {
private ClientRegistry() {
}
/**
* Register any client-side objects which don't have to be done on the main thread.
*/
public static void register() {
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided(
new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_speaker_left"),
new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_speaker_right")
));
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided(
new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_crafting_table_left"),
new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_crafting_table_right")
));
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false));
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true));
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem());
}
/**
* Register any client-side objects which must be done on the main thread.
*/
public static void registerMainThread() {
MenuScreens.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register(ModRegistry.Menus.COMPUTER.get(), GuiComputer::new);
MenuScreens.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), GuiComputer::new);
MenuScreens.<ContainerComputerBase, NoTermComputerScreen<ContainerComputerBase>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
MenuScreens.register(ModRegistry.Menus.TURTLE.get(), GuiTurtle::new);
MenuScreens.register(ModRegistry.Menus.PRINTER.get(), GuiPrinter::new);
MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), GuiDiskDrive::new);
MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), GuiPrintout::new);
MenuScreens.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), GuiComputer::new);
registerItemProperty("state",
(stack, world, player, random) -> ClientPocketComputers.get(stack).getState().ordinal(),
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
);
registerItemProperty("coloured",
(stack, world, player, random) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0,
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
);
}
@SafeVarargs
@SuppressWarnings("deprecation")
private static void registerItemProperty(String name, ItemPropertyFunction getter, Supplier<? extends Item>... items) {
var id = new ResourceLocation(ComputerCraft.MOD_ID, name);
for (var item : items) ItemProperties.register(item.get(), id, getter);
}
private static final String[] EXTRA_MODELS = new String[]{
// Turtle upgrades
"block/turtle_modem_normal_off_left",
@ -64,114 +124,63 @@ public final class ClientRegistry {
"block/turtle_elf_overlay",
};
private ClientRegistry() {
public static void registerExtraModels(Consumer<ResourceLocation> register) {
for (var model : EXTRA_MODELS) register.accept(new ResourceLocation(ComputerCraft.MOD_ID, model));
}
@SubscribeEvent
public static void registerModelLoaders(ModelEvent.RegisterGeometryLoaders event) {
event.register("turtle", TurtleModelLoader.INSTANCE);
}
@SubscribeEvent
public static void registerModels(ModelEvent.RegisterAdditional event) {
for (var model : EXTRA_MODELS) {
event.register(new ResourceLocation(ComputerCraft.MOD_ID, model));
}
}
@SubscribeEvent
public static void onItemColours(RegisterColorHandlersEvent.Item event) {
public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) {
if (ModRegistry.Items.DISK == null || ModRegistry.Blocks.TURTLE_NORMAL == null) {
ComputerCraft.log.warn("Block/item registration has failed. Skipping registration of item colours.");
return;
}
event.register(
register.accept(
(stack, layer) -> layer == 1 ? ((ItemDisk) stack.getItem()).getColour(stack) : 0xFFFFFF,
ModRegistry.Items.DISK.get()
);
event.register(
register.accept(
(stack, layer) -> layer == 1 ? ItemTreasureDisk.getColour(stack) : 0xFFFFFF,
ModRegistry.Items.TREASURE_DISK.get()
);
event.register((stack, layer) -> {
switch (layer) {
case 0:
default:
return 0xFFFFFF;
case 1: // Frame colour
return IColouredItem.getColourBasic(stack);
case 2: { // Light colour
var light = ClientPocketComputers.get(stack).getLightState();
return light == -1 ? Colour.BLACK.getHex() : light;
}
register.accept(ClientRegistry::getPocketColour, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get());
register.accept(ClientRegistry::getPocketColour, ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get());
register.accept(ClientRegistry::getTurtleColour, ModRegistry.Blocks.TURTLE_NORMAL.get());
register.accept(ClientRegistry::getTurtleColour, ModRegistry.Blocks.TURTLE_ADVANCED.get());
}
private static int getPocketColour(ItemStack stack, int layer) {
switch (layer) {
case 0:
default:
return 0xFFFFFF;
case 1: // Frame colour
return IColouredItem.getColourBasic(stack);
case 2: { // Light colour
var light = ClientPocketComputers.get(stack).getLightState();
return light == -1 ? Colour.BLACK.getHex() : light;
}
}, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get());
// Setup turtle colours
event.register(
(stack, tintIndex) -> tintIndex == 0 ? ((IColouredItem) stack.getItem()).getColour(stack) : 0xFFFFFF,
ModRegistry.Blocks.TURTLE_NORMAL.get(), ModRegistry.Blocks.TURTLE_ADVANCED.get()
);
}
@SubscribeEvent
public static void setupClient(FMLClientSetupEvent event) {
// Setup TESRs
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_NORMAL.get(), TileEntityMonitorRenderer::new);
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), TileEntityMonitorRenderer::new);
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new);
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new);
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided(
new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_speaker_left"),
new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_speaker_right")
));
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided(
new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_crafting_table_left"),
new ResourceLocation(ComputerCraft.MOD_ID, "block/turtle_crafting_table_right")
));
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false));
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true));
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem());
event.enqueueWork(() -> {
registerContainers();
registerItemProperty("state",
(stack, world, player, random) -> ClientPocketComputers.get(stack).getState().ordinal(),
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
);
registerItemProperty("coloured",
(stack, world, player, random) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0,
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
);
});
}
@SafeVarargs
private static void registerItemProperty(String name, ItemPropertyFunction getter, Supplier<? extends Item>... items) {
var id = new ResourceLocation(ComputerCraft.MOD_ID, name);
for (var item : items) {
ItemProperties.register(item.get(), id, getter);
}
}
private static int getTurtleColour(ItemStack stack, int layer) {
return layer == 0 ? ((IColouredItem) stack.getItem()).getColour(stack) : 0xFFFFFF;
}
private static void registerContainers() {
// My IDE doesn't think so, but we do actually need these generics.
public static void registerBlockEntityRenderers(BlockEntityRenderRegistry register) {
register.register(ModRegistry.BlockEntities.MONITOR_NORMAL.get(), TileEntityMonitorRenderer::new);
register.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), TileEntityMonitorRenderer::new);
register.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new);
register.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new);
}
MenuScreens.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register(ModRegistry.Menus.COMPUTER.get(), GuiComputer::new);
MenuScreens.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), GuiComputer::new);
MenuScreens.<ContainerComputerBase, NoTermComputerScreen<ContainerComputerBase>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
MenuScreens.register(ModRegistry.Menus.TURTLE.get(), GuiTurtle::new);
public interface BlockEntityRenderRegistry {
<T extends BlockEntity> void register(BlockEntityType<? extends T> type, BlockEntityRendererProvider<T> provider);
}
MenuScreens.register(ModRegistry.Menus.PRINTER.get(), GuiPrinter::new);
MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), GuiDiskDrive::new);
MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), GuiPrintout::new);
MenuScreens.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), GuiComputer::new);
public static void registerShaders(ResourceManager resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
RenderTypes.registerShaders(resources, load);
}
}

View File

@ -0,0 +1,84 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.sound.SpeakerSound;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.*;
import net.minecraftforge.client.event.sound.PlayStreamingSourceEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.level.LevelEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
/**
* Forge-specific dispatch for {@link ClientHooks}.
*/
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT)
public final class ForgeClientHooks {
private ForgeClientHooks() {
}
@SubscribeEvent
public static void onTick(TickEvent.ClientTickEvent event) {
if (event.phase == TickEvent.Phase.START) ClientHooks.onTick();
}
@SubscribeEvent
public static void onRenderTick(TickEvent.RenderTickEvent event) {
if (event.phase == TickEvent.Phase.START) ClientHooks.onRenderTick();
}
@SubscribeEvent
public static void onWorldUnload(LevelEvent.Unload event) {
if (event.getLevel().isClientSide()) ClientHooks.onWorldUnload();
}
@SubscribeEvent
public static void drawHighlight(RenderHighlightEvent.Block event) {
if (ClientHooks.drawHighlight(event.getPoseStack(), event.getMultiBufferSource(), event.getCamera(), event.getTarget())) {
event.setCanceled(true);
}
}
@SubscribeEvent
public static void onClientSendMessage(ClientChatEvent event) {
if (ClientHooks.onChatMessage(event.getMessage())) event.setCanceled(true);
}
@SubscribeEvent
public static void onRenderText(CustomizeGuiOverlayEvent.DebugText event) {
ClientHooks.addDebugInfo(event.getRight()::add);
}
@SubscribeEvent
public static void onRenderInHand(RenderHandEvent event) {
if (ClientHooks.onRenderHeldItem(
event.getPoseStack(), event.getMultiBufferSource(), event.getPackedLight(),
event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack()
)) {
event.setCanceled(true);
}
}
@SubscribeEvent
public static void onRenderInFrame(RenderItemInFrameEvent event) {
if (ClientHooks.onRenderItemFrame(
event.getPoseStack(), event.getMultiBufferSource(), event.getItemFrameEntity(), event.getItemStack(), event.getPackedLight()
)) {
event.setCanceled(true);
}
}
@SubscribeEvent
public static void playStreaming(PlayStreamingSourceEvent event) {
if (!(event.getSound() instanceof SpeakerSound sound) || sound.getStream() == null) return;
ClientHooks.onPlayStreaming(event.getEngine(), event.getChannel(), sound.getStream());
}
}

View File

@ -0,0 +1,56 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.client.render.TurtleModelLoader;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ModelEvent;
import net.minecraftforge.client.event.RegisterColorHandlersEvent;
import net.minecraftforge.client.event.RegisterShadersEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import java.io.IOException;
/**
* Registers textures and models for items.
*/
@Mod.EventBusSubscriber(modid = ComputerCraftAPI.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
public final class ForgeClientRegistry {
private ForgeClientRegistry() {
}
@SubscribeEvent
public static void registerModelLoaders(ModelEvent.RegisterGeometryLoaders event) {
event.register("turtle", TurtleModelLoader.INSTANCE);
}
@SubscribeEvent
public static void registerModels(ModelEvent.RegisterAdditional event) {
ClientRegistry.registerExtraModels(event::register);
}
@SubscribeEvent
public static void registerShaders(RegisterShadersEvent event) throws IOException {
ClientRegistry.registerShaders(event.getResourceManager(), event::registerShader);
}
@SubscribeEvent
public static void onItemColours(RegisterColorHandlersEvent.Item event) {
ClientRegistry.registerItemColours(event::register);
}
@SubscribeEvent
public static void setupClient(FMLClientSetupEvent event) {
ClientRegistry.register();
ClientRegistry.registerBlockEntityRenderers(BlockEntityRenderers::register);
event.enqueueWork(ClientRegistry::registerMainThread);
}
}

View File

@ -5,13 +5,6 @@
*/
package dan200.computercraft.client;
import dan200.computercraft.ComputerCraft;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT)
public final class FrameInfo {
private static int tick;
private static long renderFrame;
@ -27,13 +20,11 @@ public static long getRenderFrame() {
return renderFrame;
}
@SubscribeEvent
public static void onTick(TickEvent.ClientTickEvent event) {
if (event.phase == TickEvent.Phase.START) tick++;
public static void onTick() {
tick++;
}
@SubscribeEvent
public static void onRenderTick(TickEvent.RenderTickEvent event) {
if (event.phase == TickEvent.Phase.START) renderFrame++;
public static void onRenderTick() {
renderFrame++;
}
}

View File

@ -5,19 +5,17 @@
*/
package dan200.computercraft.client.render;
import dan200.computercraft.ComputerCraft;
import com.mojang.blaze3d.vertex.PoseStack;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.peripheral.modem.wired.BlockCable;
import dan200.computercraft.shared.peripheral.modem.wired.CableShapes;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.util.Mth;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderHighlightEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraft.world.phys.BlockHitResult;
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT)
public final class CableHighlightRenderer {
private CableHighlightRenderer() {
}
@ -25,37 +23,36 @@ private CableHighlightRenderer() {
/**
* Draw an outline for a specific part of a cable "Multipart".
*
* @param event The event to observe
* @param transform The current transformation matrix.
* @param bufferSource The buffer to draw to.
* @param camera The current camera.
* @param hit The block hit result for the current player.
* @return If we rendered a custom outline.
* @see net.minecraft.client.renderer.LevelRenderer#renderHitOutline
*/
@SubscribeEvent
public static void drawHighlight(RenderHighlightEvent.Block event) {
var hit = event.getTarget();
public static boolean drawHighlight(PoseStack transform, MultiBufferSource bufferSource, Camera camera, BlockHitResult hit) {
var pos = hit.getBlockPos();
var world = event.getCamera().getEntity().getCommandSenderWorld();
var info = event.getCamera();
var world = camera.getEntity().getCommandSenderWorld();
var state = world.getBlockState(pos);
// We only care about instances with both cable and modem.
if (state.getBlock() != ModRegistry.Blocks.CABLE.get() || state.getValue(BlockCable.MODEM).getFacing() == null || !state.getValue(BlockCable.CABLE)) {
return;
return false;
}
event.setCanceled(true);
var shape = WorldUtil.isVecInside(CableShapes.getModemShape(state), hit.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ()))
? CableShapes.getModemShape(state)
: CableShapes.getCableShape(state);
var cameraPos = info.getPosition();
var cameraPos = camera.getPosition();
var xOffset = pos.getX() - cameraPos.x();
var yOffset = pos.getY() - cameraPos.y();
var zOffset = pos.getZ() - cameraPos.z();
var buffer = event.getMultiBufferSource().getBuffer(RenderType.lines());
var matrix4f = event.getPoseStack().last().pose();
var normal = event.getPoseStack().last().normal();
var buffer = bufferSource.getBuffer(RenderType.lines());
var matrix4f = transform.last().pose();
var normal = transform.last().normal();
// TODO: Can we just accesstransformer out LevelRenderer.renderShape?
shape.forAllEdges((x1, y1, z1, x2, y2, z2) -> {
var xDelta = (float) (x2 - x1);
@ -77,5 +74,7 @@ public static void drawHighlight(RenderHighlightEvent.Block event) {
.normal(normal, xDelta, yDelta, zDelta)
.endVertex();
});
return true;
}
}

View File

@ -1,50 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import net.minecraft.client.Minecraft;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.CustomizeGuiOverlayEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import java.util.List;
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT)
public class DebugOverlay {
@SubscribeEvent
public static void onRenderText(CustomizeGuiOverlayEvent.DebugText event) {
var minecraft = Minecraft.getInstance();
if (!minecraft.options.renderDebug || minecraft.level == null) return;
if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return;
var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos());
if (tile instanceof TileMonitor monitor) {
event.getRight().add("");
event.getRight().add(
String.format("Targeted monitor: (%d, %d), %d x %d", monitor.getXIndex(), monitor.getYIndex(), monitor.getWidth(), monitor.getHeight())
);
} else if (tile instanceof TileTurtle turtle) {
event.getRight().add("");
event.getRight().add("Targeted turtle:");
event.getRight().add(String.format("Id: %d", turtle.getComputerID()));
addTurtleUpgrade(event.getRight(), turtle, TurtleSide.LEFT);
addTurtleUpgrade(event.getRight(), turtle, TurtleSide.RIGHT);
}
}
private static void addTurtleUpgrade(List<String> out, TileTurtle turtle, TurtleSide side) {
var upgrade = turtle.getUpgrade(side);
if (upgrade != null) out.add(String.format("Upgrade[%s]: %s", side, upgrade.getUpgradeID()));
}
}

View File

@ -30,7 +30,7 @@ public abstract class ItemMapLikeRenderer {
*/
protected abstract void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light);
protected void renderItemFirstPerson(PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) {
public void renderItemFirstPerson(PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) {
Player player = Minecraft.getInstance().player;
transform.pushPose();

View File

@ -8,7 +8,6 @@
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import com.mojang.math.Vector3f;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.pocket.ClientPocketComputers;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.util.Colour;
@ -16,10 +15,6 @@
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import static dan200.computercraft.client.render.ComputerBorderRenderer.*;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
@ -28,25 +23,12 @@
/**
* Emulates map rendering for pocket computers.
*/
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT)
public final class ItemPocketRenderer extends ItemMapLikeRenderer {
private static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer();
public static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer();
private ItemPocketRenderer() {
}
@SubscribeEvent
public static void onRenderInHand(RenderHandEvent event) {
var stack = event.getItemStack();
if (!(stack.getItem() instanceof ItemPocketComputer)) return;
event.setCanceled(true);
INSTANCE.renderItemFirstPerson(
event.getPoseStack(), event.getMultiBufferSource(), event.getPackedLight(),
event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack()
);
}
@Override
protected void renderItem(PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light) {
var computer = ClientPocketComputers.get(stack);

View File

@ -7,16 +7,11 @@
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Vector3f;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.media.items.ItemPrintout;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.decoration.ItemFrame;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.client.event.RenderItemInFrameEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import static dan200.computercraft.client.render.PrintoutRenderer.*;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
@ -27,25 +22,12 @@
/**
* Emulates map and item-frame rendering for printouts.
*/
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT)
public final class ItemPrintoutRenderer extends ItemMapLikeRenderer {
private static final ItemPrintoutRenderer INSTANCE = new ItemPrintoutRenderer();
public static final ItemPrintoutRenderer INSTANCE = new ItemPrintoutRenderer();
private ItemPrintoutRenderer() {
}
@SubscribeEvent
public static void onRenderInHand(RenderHandEvent event) {
var stack = event.getItemStack();
if (!(stack.getItem() instanceof ItemPrintout)) return;
event.setCanceled(true);
INSTANCE.renderItemFirstPerson(
event.getPoseStack(), event.getMultiBufferSource(), event.getPackedLight(),
event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack()
);
}
@Override
protected void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light) {
transform.mulPose(Vector3f.XP.rotationDegrees(180f));
@ -55,13 +37,8 @@ protected void renderItem(PoseStack transform, MultiBufferSource render, ItemSta
drawPrintout(transform, render, stack, light);
}
@SubscribeEvent
public static void onRenderInFrame(RenderItemInFrameEvent event) {
var stack = event.getItemStack();
public static void onRenderInFrame(PoseStack transform, MultiBufferSource render, ItemFrame frame, ItemStack stack, int packedLight) {
if (!(stack.getItem() instanceof ItemPrintout)) return;
event.setCanceled(true);
var transform = event.getPoseStack();
// Move a little bit forward to ensure we're not clipping with the frame
transform.translate(0.0f, 0.0f, -0.001f);
@ -69,8 +46,8 @@ public static void onRenderInFrame(RenderItemInFrameEvent event) {
transform.scale(0.95f, 0.95f, -0.95f);
transform.translate(-0.5f, -0.5f, 0.0f);
var light = event.getItemFrameEntity().getType() == EntityType.GLOW_ITEM_FRAME ? 0xf000d2 : event.getPackedLight(); // See getLightVal.
drawPrintout(transform, event.getMultiBufferSource(), stack, light);
var light = frame.getType() == EntityType.GLOW_ITEM_FRAME ? 0xf000d2 : packedLight; // See getLightVal.
drawPrintout(transform, render, stack, light);
}
private static void drawPrintout(PoseStack transform, MultiBufferSource render, ItemStack stack, int light) {

View File

@ -5,17 +5,16 @@
*/
package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.Direction;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderHighlightEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraft.world.phys.BlockHitResult;
import java.util.EnumSet;
@ -25,23 +24,19 @@
* Overrides monitor highlighting to only render the outline of the <em>whole</em> monitor, rather than the current
* block. This means you do not get an intrusive outline on top of the screen.
*/
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT)
public final class MonitorHighlightRenderer {
private MonitorHighlightRenderer() {
}
@SubscribeEvent
public static void drawHighlight(RenderHighlightEvent.Block event) {
public static boolean drawHighlight(PoseStack transformStack, MultiBufferSource bufferSource, Camera camera, BlockHitResult hit) {
// Preserve normal behaviour when crouching.
if (event.getCamera().getEntity().isCrouching()) return;
if (camera.getEntity().isCrouching()) return false;
var world = event.getCamera().getEntity().getCommandSenderWorld();
var pos = event.getTarget().getBlockPos();
var world = camera.getEntity().getCommandSenderWorld();
var pos = hit.getBlockPos();
var tile = world.getBlockEntity(pos);
if (!(tile instanceof TileMonitor monitor)) return;
event.setCanceled(true);
if (!(tile instanceof TileMonitor monitor)) return false;
// Determine which sides are part of the external faces of the monitor, and so which need to be rendered.
var faces = EnumSet.allOf(Direction.class);
@ -52,13 +47,12 @@ public static void drawHighlight(RenderHighlightEvent.Block event) {
if (monitor.getYIndex() != 0) faces.remove(monitor.getDown().getOpposite());
if (monitor.getYIndex() != monitor.getHeight() - 1) faces.remove(monitor.getDown());
var transformStack = event.getPoseStack();
var cameraPos = event.getCamera().getPosition();
var cameraPos = camera.getPosition();
transformStack.pushPose();
transformStack.translate(pos.getX() - cameraPos.x(), pos.getY() - cameraPos.y(), pos.getZ() - cameraPos.z());
// I wish I could think of a better way to do this
var buffer = event.getMultiBufferSource().getBuffer(RenderType.lines());
var buffer = bufferSource.getBuffer(RenderType.lines());
var transform = transformStack.last().pose();
var normal = transformStack.last().normal();
if (faces.contains(NORTH) || faces.contains(WEST)) line(buffer, transform, normal, 0, 0, 0, UP);
@ -75,6 +69,7 @@ public static void drawHighlight(RenderHighlightEvent.Block event) {
if (faces.contains(EAST) || faces.contains(UP)) line(buffer, transform, normal, 1, 1, 0, SOUTH);
transformStack.popPose();
return true;
}
private static void line(VertexConsumer buffer, Matrix4f transform, Matrix3f normal, float x, float y, float z, Direction direction) {

View File

@ -14,15 +14,13 @@
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RegisterShadersEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraft.server.packs.resources.ResourceManager;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
public class RenderTypes {
public static final int FULL_BRIGHT_LIGHTMAP = (0xF << 4) | (0xF << 20);
@ -62,11 +60,10 @@ static ShaderInstance getTerminalShader() {
return GameRenderer.getRendertypeTextShader();
}
@SubscribeEvent
public static void registerShaders(RegisterShadersEvent event) throws IOException {
event.registerShader(
public static void registerShaders(ResourceManager resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
load.accept(
new MonitorTextureBufferShader(
event.getResourceManager(),
resources,
new ResourceLocation(ComputerCraft.MOD_ID, "monitor_tbo"),
MONITOR_TBO.format()
),

View File

@ -5,18 +5,22 @@
*/
package dan200.computercraft.client.sound;
import com.mojang.blaze3d.audio.Channel;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.sounds.AudioStream;
import net.minecraft.client.sounds.SoundEngine;
import org.lwjgl.BufferUtils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.sound.sampled.AudioFormat;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.Executor;
class DfpwmStream implements AudioStream {
private static final int PREC = 10;
@ -26,6 +30,23 @@ class DfpwmStream implements AudioStream {
private final Queue<ByteBuffer> buffers = new ArrayDeque<>(2);
/**
* The {@link Channel} which this sound is playing on.
*
* @see SpeakerInstance#pushAudio(ByteBuf)
*/
@Nullable
Channel channel;
/**
* The underlying {@link SoundEngine} executor.
*
* @see SpeakerInstance#pushAudio(ByteBuf)
* @see SoundEngine#executor
*/
@Nullable
Executor executor;
private int charge = 0; // q
private int strength = 0; // s
private int lowPassCharge;

View File

@ -6,6 +6,7 @@
package dan200.computercraft.client.sound;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.core.util.Nullability;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
@ -32,9 +33,11 @@ public synchronized void pushAudio(ByteBuf buffer) {
currentStream.push(buffer);
// If we've got nothing left in the buffer, enqueue an additional one just in case.
if (exhausted && sound != null && sound.stream == stream && sound.channel != null) {
sound.executor.execute(() -> {
if (!sound.channel.stopped()) sound.channel.pumpBuffers(1);
if (exhausted && sound != null && sound.stream == stream && stream.channel != null && stream.executor != null) {
var actualStream = sound.stream;
stream.executor.execute(() -> {
var channel = Nullability.assertNonNull(actualStream.channel);
if (!channel.stopped()) channel.pumpBuffers(1);
});
}
}

View File

@ -5,11 +5,10 @@
*/
package dan200.computercraft.client.sound;
import com.mojang.blaze3d.audio.Channel;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.sound.PlayStreamingSourceEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraft.client.sounds.AudioStream;
import net.minecraft.client.sounds.SoundEngine;
import java.util.Map;
import java.util.UUID;
@ -18,17 +17,15 @@
/**
* Maps speakers source IDs to a {@link SpeakerInstance}.
*/
@Mod.EventBusSubscriber(Dist.CLIENT)
public class SpeakerManager {
private static final Map<UUID, SpeakerInstance> sounds = new ConcurrentHashMap<>();
@SubscribeEvent
public static void playStreaming(PlayStreamingSourceEvent event) {
if (!(event.getSound() instanceof SpeakerSound sound) || sound.stream == null) return;
public static void onPlayStreaming(SoundEngine engine, Channel channel, AudioStream stream) {
if (!(stream instanceof DfpwmStream dfpwmStream)) return;
// Associate the sound with the current channel, so SpeakerInstance.pushAudio can queue audio immediately.
sound.channel = event.getChannel();
sound.executor = event.getEngine().executor;
// Associate the stream with the current channel, so SpeakerInstance.pushAudio can queue audio immediately.
dfpwmStream.channel = channel;
dfpwmStream.executor = engine.executor;
}
public static SpeakerInstance getSound(UUID source) {

View File

@ -5,7 +5,6 @@
*/
package dan200.computercraft.client.sound;
import com.mojang.blaze3d.audio.Channel;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraft.client.resources.sounds.AbstractSoundInstance;
import net.minecraft.client.resources.sounds.Sound;
@ -19,11 +18,8 @@
import javax.annotation.Nonnull;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
public class SpeakerSound extends AbstractSoundInstance implements TickableSoundInstance {
Channel channel;
Executor executor;
DfpwmStream stream;
private Entity entity;
@ -69,4 +65,8 @@ public void tick() {
public CompletableFuture<AudioStream> getStream(@Nonnull SoundBufferLibrary soundBuffers, @Nonnull Sound sound, boolean looping) {
return stream != null ? CompletableFuture.completedFuture(stream) : super.getStream(soundBuffers, sound, looping);
}
public AudioStream getStream() {
return stream;
}
}

View File

@ -7,57 +7,52 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.shared.command.CommandComputerCraft;
import dan200.computercraft.shared.computer.core.ResourceMount;
import dan200.computercraft.shared.computer.core.ServerContext;
import dan200.computercraft.shared.computer.metrics.ComputerMBean;
import dan200.computercraft.shared.network.client.UpgradesLoadedMessage;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.peripheral.monitor.MonitorWatcher;
import dan200.computercraft.shared.util.DropConsumer;
import dan200.computercraft.shared.util.TickScheduler;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.packs.resources.PreparableReloadListener;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
import net.minecraft.world.level.storage.loot.LootPool;
import net.minecraft.world.level.storage.loot.entries.LootTableReference;
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
import net.minecraftforge.event.*;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.event.server.ServerStoppedEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.server.ServerLifecycleHooks;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer;
/**
* Miscellaneous hooks which are present on the client and server.
* Event listeners for server/common code.
* <p>
* These should possibly be refactored into separate classes at some point, but are fine here for now.
*
* @see dan200.computercraft.client.ClientHooks For client-specific ones.
* All event handlers should be defined in this class, and then invoked from a loader-specific event handler. This means
* it's much easier to ensure that each hook is called in all loader source sets.
*/
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID)
public final class CommonHooks {
private CommonHooks() {
}
@SubscribeEvent
public static void onServerTick(TickEvent.ServerTickEvent event) {
if (event.phase == TickEvent.Phase.START) {
ServerContext.get(ServerLifecycleHooks.getCurrentServer()).tick();
}
public static void onServerTickStart(MinecraftServer server) {
ServerContext.get(server).tick();
TickScheduler.tick();
}
@SubscribeEvent
public static void onRegisterCommand(RegisterCommandsEvent event) {
CommandComputerCraft.register(event.getDispatcher());
public static void onServerTickEnd() {
MonitorWatcher.onTick();
}
@SubscribeEvent
public static void onServerStarting(ServerStartingEvent event) {
var server = event.getServer();
public static void onServerStarting(MinecraftServer server) {
if (server instanceof DedicatedServer dediServer && dediServer.getProperties().enableJmxMonitoring) {
ComputerMBean.register();
}
@ -67,8 +62,7 @@ public static void onServerStarting(ServerStartingEvent event) {
ComputerMBean.start(server);
}
@SubscribeEvent
public static void onServerStopped(ServerStoppedEvent event) {
public static void onServerStopped() {
resetState();
}
@ -78,6 +72,10 @@ private static void resetState() {
NetworkUtils.reset();
}
public static void onChunkWatch(LevelChunk chunk, ServerPlayer player) {
MonitorWatcher.onWatch(chunk, player);
}
public static final ResourceLocation LOOT_TREASURE_DISK = new ResourceLocation(ComputerCraft.MOD_ID, "treasure_disk");
private static final Set<ResourceLocation> TABLES = new HashSet<>(Arrays.asList(
@ -93,32 +91,26 @@ private static void resetState() {
BuiltInLootTables.VILLAGE_CARTOGRAPHER
));
@SubscribeEvent
public static void lootLoad(LootTableLoadEvent event) {
var name = event.getName();
if (!name.getNamespace().equals("minecraft") || !TABLES.contains(name)) return;
event.getTable().addPool(LootPool.lootPool()
public static @Nullable LootPool.Builder getExtraLootPool(ResourceLocation lootTable) {
if (!lootTable.getNamespace().equals("minecraft") || !TABLES.contains(lootTable)) return null;
return LootPool.lootPool()
.add(LootTableReference.lootTableReference(LOOT_TREASURE_DISK))
.setRolls(ConstantValue.exactly(1))
.name("computercraft_treasure")
.build());
.setRolls(ConstantValue.exactly(1));
}
@SubscribeEvent
public static void onAddReloadListeners(AddReloadListenerEvent event) {
event.addListener(ResourceMount.RELOAD_LISTENER);
event.addListener(TurtleUpgrades.instance());
event.addListener(PocketUpgrades.instance());
public static void onDatapackReload(BiConsumer<String, PreparableReloadListener> addReload) {
addReload.accept("mounts", ResourceMount.RELOAD_LISTENER);
addReload.accept("turtle_upgrades", TurtleUpgrades.instance());
addReload.accept("pocket_upgrades", PocketUpgrades.instance());
}
@SubscribeEvent
public static void onDatapackSync(OnDatapackSyncEvent event) {
var packet = new UpgradesLoadedMessage();
if (event.getPlayer() == null) {
PlatformHelper.get().sendToAllPlayers(packet, event.getPlayerList().getServer());
} else {
PlatformHelper.get().sendToPlayer(packet, event.getPlayer());
}
public static boolean onEntitySpawn(Entity entity) {
return DropConsumer.onEntitySpawn(entity);
}
public static boolean onLivingDrop(Entity entity, ItemStack stack) {
return DropConsumer.onLivingDrop(entity, stack);
}
}

View File

@ -14,11 +14,8 @@
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.common.ForgeConfigSpec.ConfigValue;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.config.ModConfigEvent;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.filter.MarkerFilter;
@ -28,7 +25,6 @@
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD)
public final class Config {
private static final int MODEM_MAX_RANGE = 100000;
@ -422,19 +418,9 @@ private static void syncClient() {
ComputerCraft.uploadNagDelay = uploadNagDelay.get();
}
private static void sync(ModConfig config) {
public static void sync(ModConfig config) {
if (!config.getModId().equals(ComputerCraft.MOD_ID)) return;
if (config.getType() == ModConfig.Type.SERVER) syncServer();
if (config.getType() == ModConfig.Type.CLIENT) syncClient();
}
@SubscribeEvent
public static void sync(ModConfigEvent.Loading event) {
sync(event.getConfig());
}
@SubscribeEvent
public static void sync(ModConfigEvent.Reloading event) {
sync(event.getConfig());
}
}

View File

@ -7,7 +7,9 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.command.CommandComputerCraft;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import dan200.computercraft.shared.network.client.UpgradesLoadedMessage;
import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheral;
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
import dan200.computercraft.shared.peripheral.modem.wired.TileCable;
@ -16,13 +18,20 @@
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import dan200.computercraft.shared.peripheral.printer.TilePrinter;
import dan200.computercraft.shared.peripheral.speaker.TileSpeaker;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.util.CapabilityProvider;
import dan200.computercraft.shared.util.SidedCapabilityProvider;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.CommandBlockEntity;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.*;
import net.minecraftforge.event.entity.EntityJoinLevelEvent;
import net.minecraftforge.event.entity.living.LivingDropsEvent;
import net.minecraftforge.event.level.ChunkWatchEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.event.server.ServerStoppedEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.items.wrapper.InvWrapper;
@ -31,8 +40,54 @@
import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
import static net.minecraftforge.common.capabilities.ForgeCapabilities.ITEM_HANDLER;
/**
* Forge-specific dispatch for {@link CommonHooks}.
*/
@Mod.EventBusSubscriber(modid = ComputerCraftAPI.MOD_ID)
public class ForgeCommonHooks {
@SubscribeEvent
public static void onServerTick(TickEvent.ServerTickEvent event) {
switch (event.phase) {
case START -> CommonHooks.onServerTickStart(event.getServer());
case END -> CommonHooks.onServerTickEnd();
}
}
@SubscribeEvent
public static void onServerStarting(ServerStartingEvent event) {
CommonHooks.onServerStarting(event.getServer());
}
@SubscribeEvent
public static void onServerStopped(ServerStoppedEvent event) {
CommonHooks.onServerStopped();
}
@SubscribeEvent
public static void onRegisterCommand(RegisterCommandsEvent event) {
CommandComputerCraft.register(event.getDispatcher());
}
@SubscribeEvent
public static void onChunkWatch(ChunkWatchEvent.Watch event) {
CommonHooks.onChunkWatch(event.getChunk(), event.getPlayer());
}
@SubscribeEvent
public static void onAddReloadListeners(AddReloadListenerEvent event) {
CommonHooks.onDatapackReload((id, listener) -> event.addListener(listener));
}
@SubscribeEvent
public static void onDatapackSync(OnDatapackSyncEvent event) {
var packet = new UpgradesLoadedMessage();
if (event.getPlayer() == null) {
PlatformHelper.get().sendToAllPlayers(packet, event.getPlayerList().getServer());
} else {
PlatformHelper.get().sendToPlayer(packet, event.getPlayer());
}
}
private static final ResourceLocation PERIPHERAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "peripheral");
private static final ResourceLocation WIRED_ELEMENT = new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_node");
private static final ResourceLocation INVENTORY = new ResourceLocation(ComputerCraftAPI.MOD_ID, "inventory");
@ -83,4 +138,20 @@ public static void onCapability(AttachCapabilitiesEvent<BlockEntity> event) {
CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, () -> new CommandBlockPeripheral(commandBlock));
}
}
@SubscribeEvent
public static void lootLoad(LootTableLoadEvent event) {
var pool = CommonHooks.getExtraLootPool(event.getName());
if (pool != null) event.getTable().addPool(pool.build());
}
@SubscribeEvent(priority = EventPriority.HIGHEST)
public static void onEntitySpawn(EntityJoinLevelEvent event) {
if (CommonHooks.onEntitySpawn(event.getEntity())) event.setCanceled(true);
}
@SubscribeEvent(priority = EventPriority.LOW)
public static void onLivingDrops(LivingDropsEvent event) {
event.getDrops().removeIf(itemEntity -> CommonHooks.onLivingDrop(event.getEntity(), itemEntity.getItem()));
}
}

View File

@ -7,6 +7,7 @@
import com.mojang.brigadier.arguments.ArgumentType;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.detail.IDetailProvider;
import dan200.computercraft.api.detail.VanillaDetailRegistries;
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
@ -99,6 +100,12 @@
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Registers ComputerCraft's registry entries and additional objects, such as {@link CauldronInteraction}s and
* {@link IDetailProvider}s
* <p>
* The functions in this class should be called from a loader-specific class.
*/
public final class ModRegistry {
private static final CreativeModeTab mainItemGroup = new CreativeTabMain();
@ -354,6 +361,9 @@ private static <T extends CustomRecipe> RegistryEntry<SimpleRecipeSerializer<T>>
public static final RegistryEntry<ImpostorShapelessRecipe.Serializer> IMPOSTOR_SHAPELESS = REGISTRY.register("impostor_shapeless", ImpostorShapelessRecipe.Serializer::new);
}
/**
* Register any objects which don't have to be done on the main thread.
*/
public static void register() {
Blocks.REGISTRY.register();
BlockEntities.REGISTRY.register();
@ -379,6 +389,9 @@ public static void register() {
VanillaDetailRegistries.BLOCK_IN_WORLD.addProvider(BlockData::fill);
}
/**
* Register any objects which must be done on the main thread.
*/
public static void registerMainThread() {
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_NORMAL.get(), ItemTurtle.CAULDRON_INTERACTION);
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_ADVANCED.get(), ItemTurtle.CAULDRON_INTERACTION);

View File

@ -1,56 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.command;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.ServerContext;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.server.MinecraftServer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ClientChatEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import java.io.File;
/**
* Basic client-side commands.
* <p>
* Simply hooks into client chat messages and intercepts matching strings.
*/
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID, value = Dist.CLIENT)
public final class ClientCommands {
public static final String OPEN_COMPUTER = "/computercraft open-computer ";
private ClientCommands() {
}
@SubscribeEvent
public static void onClientSendMessage(ClientChatEvent event) {
// Emulate the command on the client side
if (event.getMessage().startsWith(OPEN_COMPUTER)) {
MinecraftServer server = Minecraft.getInstance().getSingleplayerServer();
if (server == null) return;
event.setCanceled(true);
var idStr = event.getMessage().substring(OPEN_COMPUTER.length()).trim();
int id;
try {
id = Integer.parseInt(idStr);
} catch (NumberFormatException ignore) {
return;
}
var file = new File(ServerContext.get(server).storageDir().toFile(), "computer/" + id);
if (!file.isDirectory()) return;
Util.getPlatform().openFile(file);
}
}
}

View File

@ -51,6 +51,7 @@
public final class CommandComputerCraft {
public static final UUID SYSTEM_UUID = new UUID(0, 0);
public static final String OPEN_COMPUTER = "/computercraft open-computer ";
private CommandComputerCraft() {
}
@ -310,7 +311,7 @@ private static Component linkStorage(CommandSourceStack source, int id) {
return link(
text("\u270E"),
ClientCommands.OPEN_COMPUTER + id,
OPEN_COMPUTER + id,
translate("commands.computercraft.dump.open_path")
);
}

View File

@ -12,13 +12,15 @@
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.MediaProviders;
import dan200.computercraft.shared.common.TileGeneric;
import dan200.computercraft.shared.network.client.PlayRecordClientMessage;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.util.DefaultInventory;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.RecordUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.*;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Inventory;
@ -29,6 +31,7 @@
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -408,14 +411,18 @@ private void playRecord() {
var contents = getDiskMedia();
var record = contents != null ? contents.getAudio(diskStack) : null;
if (record != null) {
RecordUtil.playRecord(record, contents.getAudioTitle(diskStack), getLevel(), getBlockPos());
playRecord(new PlayRecordClientMessage(getBlockPos(), record, contents.getAudioTitle(diskStack)));
} else {
RecordUtil.playRecord(null, null, getLevel(), getBlockPos());
stopRecord();
}
}
private void stopRecord() {
RecordUtil.playRecord(null, null, getLevel(), getBlockPos());
playRecord(new PlayRecordClientMessage(getBlockPos()));
}
private void playRecord(PlayRecordClientMessage message) {
PlatformHelper.get().sendToAllAround(message, (ServerLevel) getLevel(), Vec3.atCenterOf(getBlockPos()), 64);
}
@Override

View File

@ -10,15 +10,12 @@
import dan200.computercraft.shared.network.client.MonitorClientMessage;
import dan200.computercraft.shared.platform.PlatformHelper;
import net.minecraft.server.level.ServerLevel;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.level.ChunkWatchEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.chunk.LevelChunk;
import java.util.ArrayDeque;
import java.util.Queue;
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID)
public final class MonitorWatcher {
private static final Queue<TileMonitor> watching = new ArrayDeque<>();
@ -33,27 +30,23 @@ static void enqueue(TileMonitor monitor) {
watching.add(monitor);
}
@SubscribeEvent
public static void onWatch(ChunkWatchEvent.Watch event) {
public static void onWatch(LevelChunk chunk, ServerPlayer player) {
// Find all origin monitors who are not already on the queue and send the
// monitor data to the player.
for (var te : event.getChunk().getBlockEntities().values()) {
for (var te : chunk.getBlockEntities().values()) {
if (!(te instanceof TileMonitor monitor)) continue;
var serverMonitor = getMonitor(monitor);
if (serverMonitor == null || monitor.enqueued) continue;
var state = getState(monitor, serverMonitor);
PlatformHelper.get().sendToPlayer(new MonitorClientMessage(monitor.getBlockPos(), state), event.getPlayer());
PlatformHelper.get().sendToPlayer(new MonitorClientMessage(monitor.getBlockPos(), state), player);
}
}
@SubscribeEvent
public static void onTick(TickEvent.ServerTickEvent event) {
public static void onTick() {
// Find all enqueued monitors and send their contents to all nearby players.
if (event.phase != TickEvent.Phase.END) return;
var limit = ComputerCraft.monitorBandwidth;
var obeyLimit = limit > 0;

View File

@ -5,7 +5,6 @@
*/
package dan200.computercraft.shared.util;
import dan200.computercraft.ComputerCraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.Entity;
@ -13,17 +12,11 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.event.entity.EntityJoinLevelEvent;
import net.minecraftforge.event.entity.living.LivingDropsEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID)
public final class DropConsumer {
private DropConsumer() {
}
@ -72,22 +65,21 @@ private static void handleDrops(ItemStack stack) {
if (!remaining.isEmpty()) remainingDrops.add(remaining);
}
@SubscribeEvent(priority = EventPriority.HIGHEST)
public static void onEntitySpawn(EntityJoinLevelEvent event) {
public static boolean onEntitySpawn(Entity entity) {
// Capture any nearby item spawns
if (dropWorld == event.getLevel() && event.getEntity() instanceof ItemEntity
&& dropBounds.contains(event.getEntity().position())) {
handleDrops(((ItemEntity) event.getEntity()).getItem());
event.setCanceled(true);
if (dropWorld == entity.getLevel() && entity instanceof ItemEntity
&& dropBounds.contains(entity.position())) {
handleDrops(((ItemEntity) entity).getItem());
return true;
}
return false;
}
@SubscribeEvent(priority = EventPriority.LOW)
public static void onLivingDrops(LivingDropsEvent drops) {
if (dropEntity == null || drops.getEntity() != dropEntity) return;
public static boolean onLivingDrop(Entity entity, ItemStack stack) {
if (dropEntity == null || entity != dropEntity) return false;
for (var drop : drops.getDrops()) handleDrops(drop.getItem());
drops.getDrops().clear();
drops.setCanceled(true);
handleDrops(stack);
return true;
}
}

View File

@ -6,16 +6,10 @@
package dan200.computercraft.shared.util;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
/**
* A monotonically increasing clock which accounts for the game being paused.
*/
@Mod.EventBusSubscriber(Dist.CLIENT)
public final class PauseAwareTimer {
private static boolean paused;
private static long pauseTime;
@ -28,11 +22,7 @@ public static long getTime() {
return (paused ? pauseTime : Util.getNanos()) - pauseOffset;
}
@SubscribeEvent
public static void tick(TickEvent.RenderTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
var isPaused = Minecraft.getInstance().isPaused();
public static void tick(boolean isPaused) {
if (isPaused == paused) return;
if (isPaused) {

View File

@ -1,25 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.util;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.client.PlayRecordClientMessage;
import dan200.computercraft.shared.platform.PlatformHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
public final class RecordUtil {
private RecordUtil() {
}
public static void playRecord(SoundEvent record, String recordInfo, Level world, BlockPos pos) {
NetworkMessage packet = record != null ? new PlayRecordClientMessage(pos, record, recordInfo) : new PlayRecordClientMessage(pos);
PlatformHelper.get().sendToAllAround(packet, (ServerLevel) world, Vec3.atCenterOf(pos), 64);
}
}

View File

@ -5,14 +5,10 @@
*/
package dan200.computercraft.shared.util;
import dan200.computercraft.ComputerCraft;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
@ -23,7 +19,6 @@
* <p>
* We use this when modems and other peripherals change a block in a different thread.
*/
@Mod.EventBusSubscriber(modid = ComputerCraft.MOD_ID)
public final class TickScheduler {
private TickScheduler() {
}
@ -35,10 +30,7 @@ public static void schedule(Token token) {
if (world != null && !world.isClientSide && !token.scheduled.getAndSet(true)) toTick.add(token);
}
@SubscribeEvent
public static void tick(TickEvent.ServerTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
public static void tick() {
Token token;
while ((token = toTick.poll()) != null) {
token.scheduled.set(false);