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 {
|
plugins {
|
||||||
id 'java-library'
|
id 'java-library'
|
||||||
id 'eclipse'
|
id 'eclipse'
|
||||||
id 'idea'
|
id 'idea'
|
||||||
id 'maven-publish'
|
id 'maven-publish'
|
||||||
id 'net.neoforged.gradle.userdev' version '7.0.145'
|
id 'net.neoforged.gradle.userdev' version '7.0.145'
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('wrapper', Wrapper).configure {
|
tasks.named('wrapper', Wrapper).configure {
|
||||||
// Define wrapper values here so as to not have to always do so when updating gradlew.properties.
|
// 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
|
// 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
|
// 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.
|
// 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`)
|
// (Verify by checking gradle/wrapper/gradle-wrapper.properties to see if distributionUrl now points to `-all`)
|
||||||
distributionType = Wrapper.DistributionType.BIN
|
distributionType = Wrapper.DistributionType.BIN
|
||||||
}
|
}
|
||||||
|
|
||||||
version = mod_version
|
version = mod_version
|
||||||
group = mod_group_id
|
group = mod_group_id
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
}
|
}
|
||||||
|
|
||||||
base {
|
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.
|
// 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.
|
// Default run configurations.
|
||||||
// These can be tweaked, removed, or duplicated as needed.
|
// These can be tweaked, removed, or duplicated as needed.
|
||||||
runs {
|
runs {
|
||||||
// applies to all the run configs below
|
// applies to all the run configs below
|
||||||
configureEach {
|
configureEach {
|
||||||
// Recommended logging data for a userdev environment
|
// Recommended logging data for a userdev environment
|
||||||
// The markers can be added/remove as needed separated by commas.
|
// The markers can be added/remove as needed separated by commas.
|
||||||
// "SCAN": For mods scan.
|
// "SCAN": For mods scan.
|
||||||
// "REGISTRIES": For firing of registry events.
|
// "REGISTRIES": For firing of registry events.
|
||||||
// "REGISTRYDUMP": For getting the contents of all registries.
|
// "REGISTRYDUMP": For getting the contents of all registries.
|
||||||
systemProperty 'forge.logging.markers', 'REGISTRIES'
|
systemProperty 'forge.logging.markers', 'REGISTRIES'
|
||||||
|
|
||||||
// Recommended logging level for the console
|
// Recommended logging level for the console
|
||||||
// You can set various levels here.
|
// You can set various levels here.
|
||||||
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
|
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
|
||||||
systemProperty 'forge.logging.console.level', 'debug'
|
systemProperty 'forge.logging.console.level', 'debug'
|
||||||
|
|
||||||
modSource project.sourceSets.main
|
modSource project.sourceSets.main
|
||||||
}
|
}
|
||||||
|
|
||||||
client {
|
client {
|
||||||
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
|
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
|
||||||
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
||||||
programArgument '--nogui'
|
programArgument '--nogui'
|
||||||
}
|
}
|
||||||
|
|
||||||
// This run config launches GameTestServer and runs all registered gametests, then exits.
|
// This run config launches GameTestServer and runs all registered gametests, then exits.
|
||||||
// By default, the server will crash when no gametests are provided.
|
// 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.
|
// The gametest system is also enabled by default for other run configs under the /test command.
|
||||||
gameTestServer {
|
gameTestServer {
|
||||||
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
||||||
}
|
}
|
||||||
|
|
||||||
data {
|
data {
|
||||||
// example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it
|
// example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it
|
||||||
// workingDirectory project.file('run-data')
|
// workingDirectory project.file('run-data')
|
||||||
|
|
||||||
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
|
// 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()
|
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.
|
// 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
|
// a dependency that will be present for runtime testing but that is
|
||||||
// "optional", meaning it will not be pulled by dependents of this mod.
|
// "optional", meaning it will not be pulled by dependents of this mod.
|
||||||
configurations {
|
configurations {
|
||||||
runtimeClasspath.extendsFrom localRuntime
|
runtimeClasspath.extendsFrom localRuntime
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Specify the version of Minecraft to use.
|
// 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.
|
// 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.
|
// 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.
|
// 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.
|
// 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.
|
// 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}"
|
implementation "net.neoforged:neoforge:${neo_version}"
|
||||||
|
|
||||||
// Example optional mod dependency with JEI
|
// Example optional mod dependency with JEI
|
||||||
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
|
// 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}-common-api:${jei_version}"
|
||||||
// compileOnly "mezz.jei:jei-${mc_version}-neoforge-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
|
// 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}"
|
// localRuntime "mezz.jei:jei-${mc_version}-neoforge:${jei_version}"
|
||||||
|
|
||||||
// Example mod dependency using a mod jar from ./libs with a flat dir repository
|
// Example mod dependency using a mod jar from ./libs with a flat dir repository
|
||||||
// This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar
|
// This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar
|
||||||
// The group id is ignored when searching -- in this case, it is "blank"
|
// The group id is ignored when searching -- in this case, it is "blank"
|
||||||
// implementation "blank:coolmod-${mc_version}:${coolmod_version}"
|
// implementation "blank:coolmod-${mc_version}:${coolmod_version}"
|
||||||
|
|
||||||
// Example mod dependency using a file as dependency
|
// Example mod dependency using a file as dependency
|
||||||
// implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar")
|
// implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar")
|
||||||
|
|
||||||
// Example project dependency using a sister or child project:
|
// Example project dependency using a sister or child project:
|
||||||
// implementation project(":myproject")
|
// implementation project(":myproject")
|
||||||
|
|
||||||
// For more info:
|
// For more info:
|
||||||
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
||||||
// http://www.gradle.org/docs/current/userguide/dependency_management.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.
|
// 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.
|
// 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
|
// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html
|
||||||
tasks.withType(ProcessResources).configureEach {
|
tasks.withType(ProcessResources).configureEach {
|
||||||
var replaceProperties = [
|
var replaceProperties = [
|
||||||
minecraft_version : minecraft_version,
|
minecraft_version : minecraft_version,
|
||||||
minecraft_version_range: minecraft_version_range,
|
minecraft_version_range: minecraft_version_range,
|
||||||
neo_version : neo_version,
|
neo_version : neo_version,
|
||||||
neo_version_range : neo_version_range,
|
neo_version_range : neo_version_range,
|
||||||
loader_version_range : loader_version_range,
|
loader_version_range : loader_version_range,
|
||||||
mod_id : mod_id,
|
mod_id : mod_id,
|
||||||
mod_name : mod_name,
|
mod_name : mod_name,
|
||||||
mod_license : mod_license,
|
mod_license : mod_license,
|
||||||
mod_version : mod_version,
|
mod_version : mod_version,
|
||||||
mod_authors : mod_authors,
|
mod_authors : mod_authors,
|
||||||
mod_description : mod_description
|
mod_description : mod_description
|
||||||
]
|
]
|
||||||
inputs.properties replaceProperties
|
inputs.properties replaceProperties
|
||||||
|
|
||||||
filesMatching(['META-INF/neoforge.mods.toml']) {
|
filesMatching(['META-INF/neoforge.mods.toml']) {
|
||||||
expand replaceProperties
|
expand replaceProperties
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Example configuration to allow publishing using the maven-publish plugin
|
// Example configuration to allow publishing using the maven-publish plugin
|
||||||
publishing {
|
publishing {
|
||||||
publications {
|
publications {
|
||||||
register('mavenJava', MavenPublication) {
|
register('mavenJava', MavenPublication) {
|
||||||
from components.java
|
from components.java
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
url "file://${project.projectDir}/repo"
|
url "file://${project.projectDir}/repo"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JavaCompile).configureEach {
|
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 no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior.
|
||||||
idea {
|
idea {
|
||||||
module {
|
module {
|
||||||
downloadSources = true
|
downloadSources = true
|
||||||
downloadJavadoc = true
|
downloadJavadoc = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package dev.exhq.ajarc;
|
||||||
|
|
||||||
import dev.exhq.ajarc.config.Config;
|
import dev.exhq.ajarc.config.Config;
|
||||||
import dev.exhq.ajarc.register.Register;
|
import dev.exhq.ajarc.register.Register;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
import com.mojang.logging.LogUtils;
|
import com.mojang.logging.LogUtils;
|
||||||
|
@ -20,4 +21,8 @@ public class Ajar {
|
||||||
Register.registerAll(modEventBus);
|
Register.registerAll(modEventBus);
|
||||||
modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC);
|
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 dev.exhq.ajarc.register.NeaBlock;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.InteractionResult;
|
import net.minecraft.world.InteractionResult;
|
||||||
|
import net.minecraft.world.SimpleMenuProvider;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
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 BlockPos pPos, @NotNull Player pPlayer,
|
||||||
@NotNull BlockHitResult pHitResult) {
|
@NotNull BlockHitResult pHitResult) {
|
||||||
if (pLevel.isClientSide()) return InteractionResult.SUCCESS;
|
if (pLevel.isClientSide()) return InteractionResult.SUCCESS;
|
||||||
|
var blockEntity = (ComputerBlockEntity) pLevel.getBlockEntity(pPos);
|
||||||
|
blockEntity.openMenu(pPlayer);
|
||||||
return InteractionResult.CONSUME;
|
return InteractionResult.CONSUME;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,71 @@
|
||||||
package dev.exhq.ajarc.computer;
|
package dev.exhq.ajarc.computer;
|
||||||
|
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
import dev.exhq.ajarc.Ajar;
|
import dev.exhq.ajarc.Ajar;
|
||||||
|
import dev.exhq.ajarc.network.ComputerScreenUpdate;
|
||||||
import dev.exhq.ajarc.register.Register;
|
import dev.exhq.ajarc.register.Register;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.HolderLookup;
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.NbtOps;
|
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.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class ComputerBlockEntity extends BlockEntity {
|
public class ComputerBlockEntity extends BlockEntity {
|
||||||
public ComputerBlockEntity(BlockPos pPos, BlockState pBlockState) {
|
public ComputerBlockEntity(BlockPos pPos, BlockState pBlockState) {
|
||||||
super(Register.COMPUTER_BLOCK_ENTITY.get(), pPos, pBlockState);
|
super(Register.COMPUTER_BLOCK_ENTITY.get(), pPos, pBlockState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AjarFileSystem fileSystem = AjarFileSystem.ofBlank();
|
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
|
@Override
|
||||||
protected void loadAdditional(@NotNull CompoundTag pTag, @NotNull HolderLookup.Provider pRegistries) {
|
protected void loadAdditional(@NotNull CompoundTag pTag, @NotNull HolderLookup.Provider pRegistries) {
|
||||||
super.loadAdditional(pTag, pRegistries);
|
super.loadAdditional(pTag, pRegistries);
|
||||||
var compound = pTag.getCompound("fileSystem");
|
var mapLike = NbtOps.INSTANCE.getMap(pTag).getOrThrow();
|
||||||
fileSystem = AjarFileSystem.CODEC.codec()
|
fileSystem = fileSystemCodec.decode(NbtOps.INSTANCE, mapLike)
|
||||||
.parse(NbtOps.INSTANCE, compound)
|
.promotePartial(Ajar.LOGGER::error)
|
||||||
.resultOrPartial(Ajar.LOGGER::error)
|
.getPartialOrThrow();
|
||||||
.orElseGet(AjarFileSystem::ofBlank);
|
screen = screenCodec.decode(NbtOps.INSTANCE, mapLike)
|
||||||
|
.promotePartial(Ajar.LOGGER::error)
|
||||||
|
.getOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void saveAdditional(@NotNull CompoundTag pTag, HolderLookup.@NotNull Provider pRegistries) {
|
protected void saveAdditional(@NotNull CompoundTag pTag, HolderLookup.@NotNull Provider pRegistries) {
|
||||||
super.saveAdditional(pTag, pRegistries);
|
super.saveAdditional(pTag, pRegistries);
|
||||||
pTag.put("fileSystem",
|
var builder = NbtOps.INSTANCE.mapBuilder();
|
||||||
AjarFileSystem.CODEC
|
fileSystemCodec.encode(fileSystem, NbtOps.INSTANCE, builder);
|
||||||
.codec().encodeStart(NbtOps.INSTANCE, fileSystem)
|
screenCodec.encode(screen, NbtOps.INSTANCE, builder);
|
||||||
.getOrThrow());
|
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.ComputerBlock;
|
||||||
import dev.exhq.ajarc.computer.ComputerBlockEntity;
|
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.Holder;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.food.FoodProperties;
|
import net.minecraft.world.food.FoodProperties;
|
||||||
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import net.minecraft.world.item.CreativeModeTab;
|
import net.minecraft.world.item.CreativeModeTab;
|
||||||
import net.minecraft.world.item.CreativeModeTabs;
|
import net.minecraft.world.item.CreativeModeTabs;
|
||||||
import net.minecraft.world.item.Item;
|
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.block.state.BlockBehaviour;
|
||||||
import net.minecraft.world.level.material.MapColor;
|
import net.minecraft.world.level.material.MapColor;
|
||||||
import net.neoforged.bus.api.IEventBus;
|
import net.neoforged.bus.api.IEventBus;
|
||||||
|
import net.neoforged.neoforge.common.extensions.IMenuTypeExtension;
|
||||||
import net.neoforged.neoforge.registries.DeferredBlock;
|
import net.neoforged.neoforge.registries.DeferredBlock;
|
||||||
import net.neoforged.neoforge.registries.DeferredHolder;
|
import net.neoforged.neoforge.registries.DeferredHolder;
|
||||||
import net.neoforged.neoforge.registries.DeferredItem;
|
import net.neoforged.neoforge.registries.DeferredItem;
|
||||||
|
@ -44,6 +48,8 @@ public class Register {
|
||||||
registry(DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID));
|
registry(DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID));
|
||||||
public static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITIES =
|
public static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITIES =
|
||||||
registry(DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, MODID));
|
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>
|
||||||
|
|
||||||
/// <editor-fold desc="Registration helpers" defaultstate="collapsed">
|
/// <editor-fold desc="Registration helpers" defaultstate="collapsed">
|
||||||
|
@ -77,6 +83,8 @@ public class Register {
|
||||||
|
|
||||||
/// </editor-fold>
|
/// </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 =
|
public static final DeferredHolder<CreativeModeTab, CreativeModeTab> EXAMPLE_TAB =
|
||||||
CREATIVE_MODE_TABS.register(
|
CREATIVE_MODE_TABS.register(
|
||||||
"basic_tab",
|
"basic_tab",
|
||||||
|
@ -84,7 +92,7 @@ public class Register {
|
||||||
.builder()
|
.builder()
|
||||||
.title(Component.translatable("itemGroup.ajarcomputers.basic_tab"))
|
.title(Component.translatable("itemGroup.ajarcomputers.basic_tab"))
|
||||||
.withTabsBefore(CreativeModeTabs.COMBAT)
|
.withTabsBefore(CreativeModeTabs.COMBAT)
|
||||||
.icon(() -> EXAMPLE_ITEM.get().getDefaultInstance())
|
.icon(() -> EXAMPLE_BLOCK.asItem().getDefaultInstance())
|
||||||
.displayItems((parameters, output) -> {
|
.displayItems((parameters, output) -> {
|
||||||
ITEMS.getEntries()
|
ITEMS.getEntries()
|
||||||
.stream()
|
.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