Add screen
This commit is contained in:
parent
d7ade1a759
commit
a86c1086b7
14 changed files with 514 additions and 114 deletions
209
build.gradle
209
build.gradle
|
@ -1,29 +1,29 @@
|
|||
plugins {
|
||||
id 'java-library'
|
||||
id 'eclipse'
|
||||
id 'idea'
|
||||
id 'maven-publish'
|
||||
id 'net.neoforged.gradle.userdev' version '7.0.145'
|
||||
id 'java-library'
|
||||
id 'eclipse'
|
||||
id 'idea'
|
||||
id 'maven-publish'
|
||||
id 'net.neoforged.gradle.userdev' version '7.0.145'
|
||||
}
|
||||
|
||||
tasks.named('wrapper', Wrapper).configure {
|
||||
// Define wrapper values here so as to not have to always do so when updating gradlew.properties.
|
||||
// Switching this to Wrapper.DistributionType.ALL will download the full gradle sources that comes with
|
||||
// documentation attached on cursor hover of gradle classes and methods. However, this comes with increased
|
||||
// file size for Gradle. If you do switch this to ALL, run the Gradle wrapper task twice afterwards.
|
||||
// (Verify by checking gradle/wrapper/gradle-wrapper.properties to see if distributionUrl now points to `-all`)
|
||||
distributionType = Wrapper.DistributionType.BIN
|
||||
// Define wrapper values here so as to not have to always do so when updating gradlew.properties.
|
||||
// Switching this to Wrapper.DistributionType.ALL will download the full gradle sources that comes with
|
||||
// documentation attached on cursor hover of gradle classes and methods. However, this comes with increased
|
||||
// file size for Gradle. If you do switch this to ALL, run the Gradle wrapper task twice afterwards.
|
||||
// (Verify by checking gradle/wrapper/gradle-wrapper.properties to see if distributionUrl now points to `-all`)
|
||||
distributionType = Wrapper.DistributionType.BIN
|
||||
}
|
||||
|
||||
version = mod_version
|
||||
group = mod_group_id
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName = mod_id
|
||||
archivesName = mod_id
|
||||
}
|
||||
|
||||
// Mojang ships Java 21 to end users starting in 1.20.5, so mods should target Java 21.
|
||||
|
@ -35,47 +35,48 @@ java.toolchain.languageVersion = JavaLanguageVersion.of(21)
|
|||
// Default run configurations.
|
||||
// These can be tweaked, removed, or duplicated as needed.
|
||||
runs {
|
||||
// applies to all the run configs below
|
||||
configureEach {
|
||||
// Recommended logging data for a userdev environment
|
||||
// The markers can be added/remove as needed separated by commas.
|
||||
// "SCAN": For mods scan.
|
||||
// "REGISTRIES": For firing of registry events.
|
||||
// "REGISTRYDUMP": For getting the contents of all registries.
|
||||
systemProperty 'forge.logging.markers', 'REGISTRIES'
|
||||
// applies to all the run configs below
|
||||
configureEach {
|
||||
// Recommended logging data for a userdev environment
|
||||
// The markers can be added/remove as needed separated by commas.
|
||||
// "SCAN": For mods scan.
|
||||
// "REGISTRIES": For firing of registry events.
|
||||
// "REGISTRYDUMP": For getting the contents of all registries.
|
||||
systemProperty 'forge.logging.markers', 'REGISTRIES'
|
||||
|
||||
// Recommended logging level for the console
|
||||
// You can set various levels here.
|
||||
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
|
||||
systemProperty 'forge.logging.console.level', 'debug'
|
||||
// Recommended logging level for the console
|
||||
// You can set various levels here.
|
||||
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
|
||||
systemProperty 'forge.logging.console.level', 'debug'
|
||||
|
||||
modSource project.sourceSets.main
|
||||
}
|
||||
modSource project.sourceSets.main
|
||||
}
|
||||
|
||||
client {
|
||||
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
|
||||
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
||||
}
|
||||
client {
|
||||
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
|
||||
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
||||
}
|
||||
|
||||
server {
|
||||
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
||||
programArgument '--nogui'
|
||||
}
|
||||
server {
|
||||
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
||||
programArgument '--nogui'
|
||||
}
|
||||
|
||||
// This run config launches GameTestServer and runs all registered gametests, then exits.
|
||||
// By default, the server will crash when no gametests are provided.
|
||||
// The gametest system is also enabled by default for other run configs under the /test command.
|
||||
gameTestServer {
|
||||
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
||||
}
|
||||
// This run config launches GameTestServer and runs all registered gametests, then exits.
|
||||
// By default, the server will crash when no gametests are provided.
|
||||
// The gametest system is also enabled by default for other run configs under the /test command.
|
||||
gameTestServer {
|
||||
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
||||
}
|
||||
|
||||
data {
|
||||
// example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it
|
||||
// workingDirectory project.file('run-data')
|
||||
data {
|
||||
// example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it
|
||||
// workingDirectory project.file('run-data')
|
||||
|
||||
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
|
||||
programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath()
|
||||
}
|
||||
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
|
||||
programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(),
|
||||
'--existing', file('src/main/resources/').getAbsolutePath()
|
||||
}
|
||||
}
|
||||
|
||||
// Include resources generated by data generators.
|
||||
|
@ -86,39 +87,39 @@ sourceSets.main.resources { srcDir 'src/generated/resources' }
|
|||
// a dependency that will be present for runtime testing but that is
|
||||
// "optional", meaning it will not be pulled by dependents of this mod.
|
||||
configurations {
|
||||
runtimeClasspath.extendsFrom localRuntime
|
||||
runtimeClasspath.extendsFrom localRuntime
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Specify the version of Minecraft to use.
|
||||
// Depending on the plugin applied there are several options. We will assume you applied the userdev plugin as shown above.
|
||||
// The group for userdev is net.neoforged, the module name is neoforge, and the version is the same as the neoforge version.
|
||||
// You can however also use the vanilla plugin (net.neoforged.gradle.vanilla) to use a version of Minecraft without the neoforge loader.
|
||||
// And its provides the option to then use net.minecraft as the group, and one of; client, server or joined as the module name, plus the game version as version.
|
||||
// For all intends and purposes: You can treat this dependency as if it is a normal library you would use.
|
||||
implementation "net.neoforged:neoforge:${neo_version}"
|
||||
// Specify the version of Minecraft to use.
|
||||
// Depending on the plugin applied there are several options. We will assume you applied the userdev plugin as shown above.
|
||||
// The group for userdev is net.neoforged, the module name is neoforge, and the version is the same as the neoforge version.
|
||||
// You can however also use the vanilla plugin (net.neoforged.gradle.vanilla) to use a version of Minecraft without the neoforge loader.
|
||||
// And its provides the option to then use net.minecraft as the group, and one of; client, server or joined as the module name, plus the game version as version.
|
||||
// For all intends and purposes: You can treat this dependency as if it is a normal library you would use.
|
||||
implementation "net.neoforged:neoforge:${neo_version}"
|
||||
|
||||
// Example optional mod dependency with JEI
|
||||
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
|
||||
// compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}"
|
||||
// compileOnly "mezz.jei:jei-${mc_version}-neoforge-api:${jei_version}"
|
||||
// We add the full version to localRuntime, not runtimeOnly, so that we do not publish a dependency on it
|
||||
// localRuntime "mezz.jei:jei-${mc_version}-neoforge:${jei_version}"
|
||||
// Example optional mod dependency with JEI
|
||||
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
|
||||
// compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}"
|
||||
// compileOnly "mezz.jei:jei-${mc_version}-neoforge-api:${jei_version}"
|
||||
// We add the full version to localRuntime, not runtimeOnly, so that we do not publish a dependency on it
|
||||
// localRuntime "mezz.jei:jei-${mc_version}-neoforge:${jei_version}"
|
||||
|
||||
// Example mod dependency using a mod jar from ./libs with a flat dir repository
|
||||
// This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar
|
||||
// The group id is ignored when searching -- in this case, it is "blank"
|
||||
// implementation "blank:coolmod-${mc_version}:${coolmod_version}"
|
||||
// Example mod dependency using a mod jar from ./libs with a flat dir repository
|
||||
// This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar
|
||||
// The group id is ignored when searching -- in this case, it is "blank"
|
||||
// implementation "blank:coolmod-${mc_version}:${coolmod_version}"
|
||||
|
||||
// Example mod dependency using a file as dependency
|
||||
// implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar")
|
||||
// Example mod dependency using a file as dependency
|
||||
// implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar")
|
||||
|
||||
// Example project dependency using a sister or child project:
|
||||
// implementation project(":myproject")
|
||||
// Example project dependency using a sister or child project:
|
||||
// implementation project(":myproject")
|
||||
|
||||
// For more info:
|
||||
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
||||
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
||||
// For more info:
|
||||
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
||||
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
||||
}
|
||||
|
||||
// This block of code expands all declared replace properties in the specified resource targets.
|
||||
|
@ -126,48 +127,48 @@ dependencies {
|
|||
// When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments.
|
||||
// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html
|
||||
tasks.withType(ProcessResources).configureEach {
|
||||
var replaceProperties = [
|
||||
minecraft_version : minecraft_version,
|
||||
minecraft_version_range: minecraft_version_range,
|
||||
neo_version : neo_version,
|
||||
neo_version_range : neo_version_range,
|
||||
loader_version_range : loader_version_range,
|
||||
mod_id : mod_id,
|
||||
mod_name : mod_name,
|
||||
mod_license : mod_license,
|
||||
mod_version : mod_version,
|
||||
mod_authors : mod_authors,
|
||||
mod_description : mod_description
|
||||
]
|
||||
inputs.properties replaceProperties
|
||||
var replaceProperties = [
|
||||
minecraft_version : minecraft_version,
|
||||
minecraft_version_range: minecraft_version_range,
|
||||
neo_version : neo_version,
|
||||
neo_version_range : neo_version_range,
|
||||
loader_version_range : loader_version_range,
|
||||
mod_id : mod_id,
|
||||
mod_name : mod_name,
|
||||
mod_license : mod_license,
|
||||
mod_version : mod_version,
|
||||
mod_authors : mod_authors,
|
||||
mod_description : mod_description
|
||||
]
|
||||
inputs.properties replaceProperties
|
||||
|
||||
filesMatching(['META-INF/neoforge.mods.toml']) {
|
||||
expand replaceProperties
|
||||
}
|
||||
filesMatching(['META-INF/neoforge.mods.toml']) {
|
||||
expand replaceProperties
|
||||
}
|
||||
}
|
||||
|
||||
// Example configuration to allow publishing using the maven-publish plugin
|
||||
publishing {
|
||||
publications {
|
||||
register('mavenJava', MavenPublication) {
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url "file://${project.projectDir}/repo"
|
||||
}
|
||||
}
|
||||
publications {
|
||||
register('mavenJava', MavenPublication) {
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url "file://${project.projectDir}/repo"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||
}
|
||||
|
||||
// IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior.
|
||||
idea {
|
||||
module {
|
||||
downloadSources = true
|
||||
downloadJavadoc = true
|
||||
}
|
||||
module {
|
||||
downloadSources = true
|
||||
downloadJavadoc = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package dev.exhq.ajarc;
|
|||
|
||||
import dev.exhq.ajarc.config.Config;
|
||||
import dev.exhq.ajarc.register.Register;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
@ -20,4 +21,8 @@ public class Ajar {
|
|||
Register.registerAll(modEventBus);
|
||||
modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC);
|
||||
}
|
||||
|
||||
public static ResourceLocation identifier(String path) {
|
||||
return ResourceLocation.fromNamespaceAndPath(MODID, path);
|
||||
}
|
||||
}
|
||||
|
|
16
src/main/java/dev/exhq/ajarc/ClientEvents.java
Normal file
16
src/main/java/dev/exhq/ajarc/ClientEvents.java
Normal file
|
@ -0,0 +1,16 @@
|
|||
package dev.exhq.ajarc;
|
||||
|
||||
import dev.exhq.ajarc.computer.ComputerScreen;
|
||||
import dev.exhq.ajarc.register.Register;
|
||||
import net.neoforged.api.distmarker.Dist;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.common.EventBusSubscriber;
|
||||
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent;
|
||||
|
||||
@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD, modid = Ajar.MODID, value = Dist.CLIENT)
|
||||
public class ClientEvents {
|
||||
@SubscribeEvent
|
||||
public static void registerScreens(RegisterMenuScreensEvent event) {
|
||||
event.register(Register.COMPUTER_MENU.get(), ComputerScreen::new);
|
||||
}
|
||||
}
|
16
src/main/java/dev/exhq/ajarc/ModEvents.java
Normal file
16
src/main/java/dev/exhq/ajarc/ModEvents.java
Normal file
|
@ -0,0 +1,16 @@
|
|||
package dev.exhq.ajarc;
|
||||
|
||||
import dev.exhq.ajarc.network.ComputerScreenUpdate;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.common.EventBusSubscriber;
|
||||
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
|
||||
|
||||
@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD, modid = Ajar.MODID)
|
||||
public class ModEvents {
|
||||
@SubscribeEvent
|
||||
public static void register(RegisterPayloadHandlersEvent event) {
|
||||
var registrar = event.registrar("1");
|
||||
registrar.playToClient(ComputerScreenUpdate.TYPE, ComputerScreenUpdate.STREAM_CODEC, ComputerScreenUpdate::handle);
|
||||
}
|
||||
|
||||
}
|
|
@ -2,7 +2,9 @@ package dev.exhq.ajarc.computer;
|
|||
|
||||
import dev.exhq.ajarc.register.NeaBlock;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.SimpleMenuProvider;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
|
@ -30,6 +32,8 @@ public class ComputerBlock extends NeaBlock implements EntityBlock {
|
|||
@NotNull BlockPos pPos, @NotNull Player pPlayer,
|
||||
@NotNull BlockHitResult pHitResult) {
|
||||
if (pLevel.isClientSide()) return InteractionResult.SUCCESS;
|
||||
var blockEntity = (ComputerBlockEntity) pLevel.getBlockEntity(pPos);
|
||||
blockEntity.openMenu(pPlayer);
|
||||
return InteractionResult.CONSUME;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,71 @@
|
|||
package dev.exhq.ajarc.computer;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import dev.exhq.ajarc.Ajar;
|
||||
import dev.exhq.ajarc.network.ComputerScreenUpdate;
|
||||
import dev.exhq.ajarc.register.Register;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.SimpleMenuProvider;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ComputerBlockEntity extends BlockEntity {
|
||||
public ComputerBlockEntity(BlockPos pPos, BlockState pBlockState) {
|
||||
super(Register.COMPUTER_BLOCK_ENTITY.get(), pPos, pBlockState);
|
||||
}
|
||||
|
||||
private AjarFileSystem fileSystem = AjarFileSystem.ofBlank();
|
||||
private ComputerTerminal screen = ComputerTerminal.ofSize(20, 30);
|
||||
|
||||
private static final MapCodec<AjarFileSystem> fileSystemCodec = AjarFileSystem.CODEC
|
||||
.fieldOf("fileSystem")
|
||||
.setPartial(AjarFileSystem::ofBlank);
|
||||
private static final MapCodec<ComputerTerminal> screenCodec = ComputerTerminal.CODEC
|
||||
.fieldOf("screen")
|
||||
.setPartial(() -> ComputerTerminal.ofSize(20, 30));
|
||||
|
||||
@Override
|
||||
protected void loadAdditional(@NotNull CompoundTag pTag, @NotNull HolderLookup.Provider pRegistries) {
|
||||
super.loadAdditional(pTag, pRegistries);
|
||||
var compound = pTag.getCompound("fileSystem");
|
||||
fileSystem = AjarFileSystem.CODEC.codec()
|
||||
.parse(NbtOps.INSTANCE, compound)
|
||||
.resultOrPartial(Ajar.LOGGER::error)
|
||||
.orElseGet(AjarFileSystem::ofBlank);
|
||||
var mapLike = NbtOps.INSTANCE.getMap(pTag).getOrThrow();
|
||||
fileSystem = fileSystemCodec.decode(NbtOps.INSTANCE, mapLike)
|
||||
.promotePartial(Ajar.LOGGER::error)
|
||||
.getPartialOrThrow();
|
||||
screen = screenCodec.decode(NbtOps.INSTANCE, mapLike)
|
||||
.promotePartial(Ajar.LOGGER::error)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveAdditional(@NotNull CompoundTag pTag, HolderLookup.@NotNull Provider pRegistries) {
|
||||
super.saveAdditional(pTag, pRegistries);
|
||||
pTag.put("fileSystem",
|
||||
AjarFileSystem.CODEC
|
||||
.codec().encodeStart(NbtOps.INSTANCE, fileSystem)
|
||||
.getOrThrow());
|
||||
var builder = NbtOps.INSTANCE.mapBuilder();
|
||||
fileSystemCodec.encode(fileSystem, NbtOps.INSTANCE, builder);
|
||||
screenCodec.encode(screen, NbtOps.INSTANCE, builder);
|
||||
builder.build((CompoundTag) null).resultOrPartial(Ajar.LOGGER::error)
|
||||
.ifPresent(it -> pTag.merge((CompoundTag) it));
|
||||
}
|
||||
|
||||
public void openMenu(Player pPlayer) {
|
||||
pPlayer.openMenu(new SimpleMenuProvider(
|
||||
(pContainerId, pPlayerInventory, pPlayer1) -> new ComputerMenu(pContainerId, this),
|
||||
Component.translatable("ajarc.computer.screen")
|
||||
), buf -> ComputerScreenUpdate.STREAM_CODEC.encode(buf, getSyncPacket(0)));
|
||||
}
|
||||
|
||||
public ComputerTerminal getTerminal() {
|
||||
return screen;
|
||||
}
|
||||
|
||||
public ComputerScreenUpdate getSyncPacket(int windowId) {
|
||||
return new ComputerScreenUpdate(windowId, Arrays.asList("Hiii", "Hello"), screen.dimensions().rows(), screen.dimensions().columns());
|
||||
}
|
||||
}
|
||||
|
|
71
src/main/java/dev/exhq/ajarc/computer/ComputerMenu.java
Normal file
71
src/main/java/dev/exhq/ajarc/computer/ComputerMenu.java
Normal file
|
@ -0,0 +1,71 @@
|
|||
package dev.exhq.ajarc.computer;
|
||||
|
||||
import dev.exhq.ajarc.network.ComputerScreenUpdate;
|
||||
import dev.exhq.ajarc.register.Register;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ComputerMenu extends AbstractContainerMenu {
|
||||
|
||||
private ComputerBlockEntity computer;
|
||||
private int columns = 80;
|
||||
private int rows = 40;
|
||||
private List<String> lines = new ArrayList<>();
|
||||
|
||||
public ComputerMenu(int pContainerId, ComputerBlockEntity computer) {
|
||||
this(pContainerId);
|
||||
this.computer = computer;
|
||||
this.syncFromComputer();
|
||||
}
|
||||
|
||||
public ComputerMenu(int pContainerId) {
|
||||
super(Register.COMPUTER_MENU.get(), pContainerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack quickMoveStack(@NotNull Player pPlayer, int pIndex) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(@NotNull Player pPlayer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getColumns() {
|
||||
return columns;
|
||||
}
|
||||
|
||||
public int getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
public List<String> getLines() {
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
||||
public void syncFromComputer() {
|
||||
// TODO: make this automatic using some mechanism
|
||||
// Maybe compare an int and update that int everytime a change is done in the computer
|
||||
// Maybe even replace all that with getUpdatePacket() and read from the block entity
|
||||
this.columns = computer.getTerminal().dimensions().columns();
|
||||
this.rows = computer.getTerminal().dimensions().rows();
|
||||
}
|
||||
|
||||
public void syncToClient(ServerPlayer player) {
|
||||
player.connection.send(this.computer.getSyncPacket(containerId));
|
||||
}
|
||||
|
||||
public void updateScreen(ComputerScreenUpdate computerScreenUpdate) {
|
||||
this.columns = computerScreenUpdate.columns();
|
||||
this.rows = computerScreenUpdate.rows();
|
||||
this.lines = computerScreenUpdate.lines();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package dev.exhq.ajarc.computer;
|
||||
|
||||
import dev.exhq.ajarc.network.ComputerScreenUpdate;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.neoforged.neoforge.network.IContainerFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ComputerMenuFactory implements IContainerFactory<ComputerMenu> {
|
||||
@Override
|
||||
public @NotNull ComputerMenu create(int pContainerId, @NotNull Inventory pPlayerInventory, @NotNull RegistryFriendlyByteBuf data) {
|
||||
var menu = new ComputerMenu(pContainerId);
|
||||
var update = ComputerScreenUpdate.STREAM_CODEC.decode(data);
|
||||
menu.updateScreen(update);
|
||||
return menu;
|
||||
}
|
||||
}
|
36
src/main/java/dev/exhq/ajarc/computer/ComputerScreen.java
Normal file
36
src/main/java/dev/exhq/ajarc/computer/ComputerScreen.java
Normal file
|
@ -0,0 +1,36 @@
|
|||
package dev.exhq.ajarc.computer;
|
||||
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ComputerScreen extends AbstractContainerScreen<ComputerMenu> {
|
||||
public ComputerScreen(ComputerMenu pMenu, Inventory pPlayerInventory, Component pTitle) {
|
||||
super(pMenu, pPlayerInventory, pTitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
imageHeight = menu.getRows() * 11;
|
||||
imageWidth = menu.getColumns() * 10;
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBg(@NotNull GuiGraphics graphics, float pPartialTick, int pMouseX, int pMouseY) {
|
||||
graphics.fill(leftPos, topPos, leftPos + imageWidth, topPos + imageHeight,
|
||||
0xFF000000);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderLabels(@NotNull GuiGraphics graphics, int pMouseX, int pMouseY) {
|
||||
int offsetY = 0;
|
||||
for (String line : menu.getLines()) {
|
||||
graphics.drawString(font, line, 0, offsetY, 0xFFFFFFFF);
|
||||
offsetY += 11;
|
||||
}
|
||||
}
|
||||
}
|
35
src/main/java/dev/exhq/ajarc/computer/ComputerTerminal.java
Normal file
35
src/main/java/dev/exhq/ajarc/computer/ComputerTerminal.java
Normal file
|
@ -0,0 +1,35 @@
|
|||
package dev.exhq.ajarc.computer;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import net.minecraft.Util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record ComputerTerminal(
|
||||
Dimensions dimensions
|
||||
) {
|
||||
public static ComputerTerminal ofSize(int rows, int columns) {
|
||||
return new ComputerTerminal(new Dimensions(rows, columns));
|
||||
}
|
||||
|
||||
public record Dimensions(
|
||||
int rows, int columns
|
||||
) {
|
||||
public static final Codec<Dimensions> CODEC =
|
||||
Codec.INT.listOf()
|
||||
.comapFlatMap(
|
||||
list -> Util.fixedSize(list, 2)
|
||||
.map(fixedList -> new Dimensions(fixedList.get(0), fixedList.get(1))),
|
||||
dimensions -> List.of(dimensions.rows, dimensions.columns)
|
||||
);
|
||||
}
|
||||
|
||||
public static final Codec<ComputerTerminal> CODEC =
|
||||
RecordCodecBuilder.create(
|
||||
builder ->
|
||||
builder.group(
|
||||
ComputerTerminal.Dimensions.CODEC.fieldOf("dimensions").forGetter(ComputerTerminal::dimensions)
|
||||
).apply(builder, ComputerTerminal::new));
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package dev.exhq.ajarc.network;
|
||||
|
||||
import dev.exhq.ajarc.Ajar;
|
||||
import dev.exhq.ajarc.computer.ComputerMenu;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public record ComputerScreenUpdate(
|
||||
int windowId,
|
||||
List<String> lines,
|
||||
int rows,
|
||||
int columns
|
||||
) implements CustomPacketPayload {
|
||||
public static final StreamCodec<ByteBuf, ComputerScreenUpdate> STREAM_CODEC =
|
||||
StreamCodec.composite(
|
||||
ByteBufCodecs.VAR_INT,
|
||||
ComputerScreenUpdate::windowId,
|
||||
ByteBufCodecs.collection(ArrayList::new, ByteBufCodecs.STRING_UTF8),
|
||||
ComputerScreenUpdate::lines,
|
||||
ByteBufCodecs.VAR_INT,
|
||||
ComputerScreenUpdate::rows,
|
||||
ByteBufCodecs.VAR_INT,
|
||||
ComputerScreenUpdate::columns,
|
||||
ComputerScreenUpdate::new
|
||||
);
|
||||
|
||||
public static final Type<ComputerScreenUpdate> TYPE = new Type<>(Ajar.identifier("screen"));
|
||||
|
||||
@Override
|
||||
public @NotNull Type<? extends CustomPacketPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public void handle(IPayloadContext iPayloadContext) {
|
||||
var menu = iPayloadContext.player().containerMenu;
|
||||
if (menu instanceof ComputerMenu computerMenu && computerMenu.containerId == windowId) {
|
||||
computerMenu.updateScreen(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,13 @@ package dev.exhq.ajarc.register;
|
|||
|
||||
import dev.exhq.ajarc.computer.ComputerBlock;
|
||||
import dev.exhq.ajarc.computer.ComputerBlockEntity;
|
||||
import dev.exhq.ajarc.computer.ComputerMenu;
|
||||
import dev.exhq.ajarc.computer.ComputerMenuFactory;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.food.FoodProperties;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.CreativeModeTabs;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
@ -13,6 +16,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
|
|||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.material.MapColor;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.neoforge.common.extensions.IMenuTypeExtension;
|
||||
import net.neoforged.neoforge.registries.DeferredBlock;
|
||||
import net.neoforged.neoforge.registries.DeferredHolder;
|
||||
import net.neoforged.neoforge.registries.DeferredItem;
|
||||
|
@ -44,6 +48,8 @@ public class Register {
|
|||
registry(DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID));
|
||||
public static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITIES =
|
||||
registry(DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, MODID));
|
||||
public static final DeferredRegister<MenuType<?>> MENU_TYPES =
|
||||
registry(DeferredRegister.create(Registries.MENU, MODID));
|
||||
/// </editor-fold>
|
||||
|
||||
/// <editor-fold desc="Registration helpers" defaultstate="collapsed">
|
||||
|
@ -77,6 +83,8 @@ public class Register {
|
|||
|
||||
/// </editor-fold>
|
||||
|
||||
public static final Supplier<MenuType<ComputerMenu>> COMPUTER_MENU = MENU_TYPES.register("computer", () -> IMenuTypeExtension.create(new ComputerMenuFactory()));
|
||||
|
||||
public static final DeferredHolder<CreativeModeTab, CreativeModeTab> EXAMPLE_TAB =
|
||||
CREATIVE_MODE_TABS.register(
|
||||
"basic_tab",
|
||||
|
@ -84,7 +92,7 @@ public class Register {
|
|||
.builder()
|
||||
.title(Component.translatable("itemGroup.ajarcomputers.basic_tab"))
|
||||
.withTabsBefore(CreativeModeTabs.COMBAT)
|
||||
.icon(() -> EXAMPLE_ITEM.get().getDefaultInstance())
|
||||
.icon(() -> EXAMPLE_BLOCK.asItem().getDefaultInstance())
|
||||
.displayItems((parameters, output) -> {
|
||||
ITEMS.getEntries()
|
||||
.stream()
|
||||
|
|
22
src/main/java/dev/exhq/ajarc/util/AjarCodecUtil.java
Normal file
22
src/main/java/dev/exhq/ajarc/util/AjarCodecUtil.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
package dev.exhq.ajarc.util;
|
||||
|
||||
import com.mojang.serialization.DataResult;
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.MapLike;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class AjarCodecUtil {
|
||||
public static <T> Optional<T> getOrPartialAndLog(DataResult<T> result, Logger logger) {
|
||||
return result.resultOrPartial(logger::error);
|
||||
}
|
||||
|
||||
public static <T, Ops> T parseVariable(
|
||||
MapCodec<T> codec, DynamicOps<Ops> ops, MapLike<Ops> mapLike, Logger logger) {
|
||||
return getOrPartialAndLog(codec.decode(ops, mapLike), logger)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Provided MapCodec does not have a partial"));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package dev.exhq.ajarc.util;
|
||||
|
||||
import com.mojang.serialization.DataResult;
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.mojang.serialization.RecordBuilder;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public class NbtMergingRecordBuilder implements RecordBuilder<Tag> {
|
||||
private DataResult<CompoundTag> result;
|
||||
private final CompoundTag tag;
|
||||
|
||||
private NbtMergingRecordBuilder(CompoundTag tag) {
|
||||
this.result = DataResult.success(tag);
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public static NbtMergingRecordBuilder ofWrapping(CompoundTag tag) {
|
||||
return new NbtMergingRecordBuilder(tag);
|
||||
}
|
||||
|
||||
|
||||
private <T> DataResult<T> mergeError(DataResult<T> errorBearer) {
|
||||
if (errorBearer.isError()) {
|
||||
result = result.flatMap(partialValue -> errorBearer.map(ignored -> partialValue));
|
||||
}
|
||||
return errorBearer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicOps<Tag> ops() {
|
||||
return NbtOps.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecordBuilder<Tag> add(Tag key, Tag value) {
|
||||
return add(key, DataResult.success(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecordBuilder<Tag> add(Tag key, DataResult<Tag> value) {
|
||||
return add(DataResult.success(key), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecordBuilder<Tag> add(DataResult<Tag> key, DataResult<Tag> value) {
|
||||
DataResult<String> stringValue = mergeError(key.flatMap(ops()::getStringValue));
|
||||
mergeError(value);
|
||||
value.ifSuccess(
|
||||
unboxedValue ->
|
||||
stringValue.ifSuccess(
|
||||
stringKey -> {
|
||||
if (tag.contains(stringKey))
|
||||
mergeError(DataResult.error(
|
||||
() -> "Found duplicate key " + stringKey + " during nbt record building"));
|
||||
else tag.put(stringKey, unboxedValue);
|
||||
}
|
||||
));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecordBuilder<Tag> withErrorsFrom(DataResult<?> result) {
|
||||
mergeError(result);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecordBuilder<Tag> setLifecycle(Lifecycle lifecycle) {
|
||||
result.setLifecycle(lifecycle);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecordBuilder<Tag> mapError(UnaryOperator<String> onError) {
|
||||
result.mapError(onError);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataResult<Tag> build(Tag prefix) {
|
||||
return result.map(it -> it);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue