Skip to content

Commit

Permalink
Finish up in world view for pocket computers, some other tweaks and f…
Browse files Browse the repository at this point in the history
…ixes
  • Loading branch information
Patbox committed Mar 20, 2024
1 parent 970a27a commit 15c14b6
Show file tree
Hide file tree
Showing 11 changed files with 331 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import eu.pb4.cctpatch.impl.poly.Fonts;
import eu.pb4.cctpatch.impl.config.PatchConfig;
import eu.pb4.cctpatch.impl.poly.font.Fonts;
import eu.pb4.cctpatch.impl.poly.PolymerSetup;
import eu.pb4.cctpatch.impl.poly.model.TurtleModel;
import eu.pb4.cctpatch.impl.poly.textures.GuiTextures;
Expand All @@ -18,7 +19,6 @@
import javax.imageio.ImageIO;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class ComputerCraftPolymerPatch implements ModInitializer {
Expand All @@ -30,14 +30,14 @@ public class ComputerCraftPolymerPatch implements ModInitializer {
public void onInitialize() {
Fonts.TERMINAL_FONT.hashCode();
GuiTextures.ADVANCED_COMPUTER.hashCode();

TurtleModel.CRAFTING_MODEL.left();
PatchConfig.instance.hashCode();

PolymerResourcePackUtils.addModAssets("computercraft");
PolymerResourcePackUtils.addModAssets(MOD_ID);

ServerLifecycleEvents.SERVER_STARTING.register((server1 -> server = server1));
ServerLifecycleEvents.SERVER_STOPPED.register((server1 -> server = null));
ServerLifecycleEvents.END_DATA_PACK_RELOAD.register(((a, b, c) -> PatchConfig.loadOrCreateConfig()));

PolymerResourcePackUtils.RESOURCE_PACK_CREATION_EVENT.register(builder -> {
builder.addWriteConverter((path, data) -> {
Expand Down
133 changes: 133 additions & 0 deletions src/main/java/eu/pb4/cctpatch/impl/config/BaseGson.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package eu.pb4.cctpatch.impl.config;

import com.google.gson.*;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.decoration.Brightness;
import net.minecraft.entity.decoration.DisplayEntity;
import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.*;
import net.minecraft.sound.SoundEvent;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.AffineTransformation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec2f;
import net.minecraft.util.math.Vec3d;

import java.lang.reflect.Type;
import java.util.List;
import java.util.function.Function;

public class BaseGson {
private static final RegistryWrapper.WrapperLookup GLOBAL_REGISTRIES = DynamicRegistryManager.of(Registries.REGISTRIES);

public static final Gson GSON = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting()
.enableComplexMapKeySerialization()
.registerTypeHierarchyAdapter(Identifier.class, new Identifier.Serializer())

.registerTypeHierarchyAdapter(Item.class, new RegistrySerializer<>(Registries.ITEM))
.registerTypeHierarchyAdapter(Block.class, new RegistrySerializer<>(Registries.BLOCK))
.registerTypeHierarchyAdapter(Enchantment.class, new RegistrySerializer<>(Registries.ENCHANTMENT))
.registerTypeHierarchyAdapter(SoundEvent.class, new RegistrySerializer<>(Registries.SOUND_EVENT))
.registerTypeHierarchyAdapter(StatusEffect.class, new RegistrySerializer<>(Registries.STATUS_EFFECT))
.registerTypeHierarchyAdapter(EntityType.class, new RegistrySerializer<>(Registries.ENTITY_TYPE))
.registerTypeHierarchyAdapter(BlockEntityType.class, new RegistrySerializer<>(Registries.BLOCK_ENTITY_TYPE))

.registerTypeHierarchyAdapter(Text.class, new Text.Serializer())

//.registerTypeHierarchyAdapter(ItemStack.class, new CodecSerializer<>(ItemStack.CODEC))
.registerTypeHierarchyAdapter(ItemStack.class, new ItemStackSerializer())
.registerTypeHierarchyAdapter(NbtCompound.class, new CodecSerializer<>(NbtCompound.CODEC))
.registerTypeHierarchyAdapter(BlockPos.class, new CodecSerializer<>(BlockPos.CODEC))
.registerTypeHierarchyAdapter(Vec3d.class, new CodecSerializer<>(Vec3d.CODEC))
.registerTypeHierarchyAdapter(Vec2f.class, new CodecSerializer<>(Codec.list(Codec.DOUBLE).xmap(x -> new Vec2f(x.get(0).floatValue(), x.get(1).floatValue()), x -> List.of((double) x.x, (double) x.y))))
.registerTypeHierarchyAdapter(EntityDimensions.class, new CodecSerializer<>(Codec.list(Codec.DOUBLE).xmap(x -> EntityDimensions.fixed(x.get(0).floatValue(), x.get(1).floatValue()), x -> List.of((double) x.width, (double) x.height))))
.registerTypeHierarchyAdapter(BlockState.class, new CodecSerializer<>(BlockState.CODEC))
.registerTypeHierarchyAdapter(AffineTransformation.class, new CodecSerializer<>(AffineTransformation.CODEC))
.registerTypeHierarchyAdapter(DisplayEntity.BillboardMode.class, new CodecSerializer<>(DisplayEntity.BillboardMode.CODEC))
.registerTypeHierarchyAdapter(DisplayEntity.TextDisplayEntity.TextAlignment.class, new CodecSerializer<>(DisplayEntity.TextDisplayEntity.TextAlignment.CODEC))
.registerTypeHierarchyAdapter(Brightness.class, new CodecSerializer<>(Brightness.CODEC))

//.registerTypeHierarchyAdapter(Matrix4f.class, new CodecSerializer<>(AffineTransformation.ANY_CODEC.xmap(AffineTransformation::getMatrix, AffineTransformation::new)))
.setLenient().create();
private record ItemStackSerializer() implements JsonSerializer<ItemStack>, JsonDeserializer<ItemStack> {
@Override
public ItemStack deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
if (jsonElement.isJsonObject()) {
return ItemStack.CODEC.decode(RegistryOps.of(JsonOps.INSTANCE, GLOBAL_REGISTRIES), jsonElement).result().orElse(Pair.of(ItemStack.EMPTY, null)).getFirst();
} else {
return Registries.ITEM.get(Identifier.tryParse(jsonElement.getAsString())).getDefaultStack();
}
}

@Override
public JsonElement serialize(ItemStack stack, Type type, JsonSerializationContext jsonSerializationContext) {
if (stack.getCount() == 1 && !stack.hasNbt()) {
return new JsonPrimitive(Registries.ITEM.getId(stack.getItem()).toString());
}

return ItemStack.CODEC.encodeStart(RegistryOps.of(JsonOps.INSTANCE, GLOBAL_REGISTRIES), stack).result().orElse(null);
}
}

private record StringSerializer<T>(Function<String, T> decode, Function<T, String> encode) implements JsonSerializer<T>, JsonDeserializer<T> {
@Override
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonPrimitive()) {
return this.decode.apply(json.getAsString());
}
return null;
}

@Override
public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(this.encode.apply(src));
}
}

private record RegistrySerializer<T>(Registry<T> registry) implements JsonSerializer<T>, JsonDeserializer<T> {
@Override
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonPrimitive()) {
return this.registry.get(Identifier.tryParse(json.getAsString()));
}
return null;
}

@Override
public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive("" + this.registry.getId(src));
}
}

private record CodecSerializer<T>(Codec<T> codec) implements JsonSerializer<T>, JsonDeserializer<T> {
@Override
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
return this.codec.decode(JsonOps.INSTANCE, json).getOrThrow(false, (x) -> {}).getFirst();
} catch (Throwable e) {
return null;
}
}

@Override
public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context) {
try {
return src != null ? this.codec.encodeStart(JsonOps.INSTANCE, src).getOrThrow(false, (x) -> {}) : JsonNull.INSTANCE;
} catch (Throwable e) {
return JsonNull.INSTANCE;
}
}
}
}
47 changes: 47 additions & 0 deletions src/main/java/eu/pb4/cctpatch/impl/config/PatchConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package eu.pb4.cctpatch.impl.config;


import com.google.gson.annotations.SerializedName;
import eu.pb4.cctpatch.impl.ComputerCraftPolymerPatch;
import net.fabricmc.loader.api.FabricLoader;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;

public final class PatchConfig {
public static PatchConfig instance = loadOrCreateConfig();
@SerializedName("display_pocket_computer_screen_in_hand")
public boolean displayPocketComputerScreenInHand = true;

public static PatchConfig loadOrCreateConfig() {
try {
PatchConfig config;
var configFile = FabricLoader.getInstance().getConfigDir().resolve(ComputerCraftPolymerPatch.MOD_ID + ".json");

if (Files.exists(configFile)) {
config = BaseGson.GSON.fromJson(Files.readString(configFile, StandardCharsets.UTF_8), PatchConfig.class);
} else {
config = new PatchConfig();
}

saveConfig(config);
return instance = config;
}
catch(IOException exception) {
ComputerCraftPolymerPatch.LOGGER.error("Something went wrong while reading the config!", exception);
return instance = new PatchConfig();
}
}

public static void saveConfig(PatchConfig config) {
var configFile = FabricLoader.getInstance().getConfigDir().resolve(ComputerCraftPolymerPatch.MOD_ID + ".json");
try {
Files.writeString(configFile, BaseGson.GSON.toJson(config), StandardCharsets.UTF_8, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
} catch (Exception e) {
ComputerCraftPolymerPatch.LOGGER.error("Something went wrong while saving the config!", e);
}
}
}
109 changes: 99 additions & 10 deletions src/main/java/eu/pb4/cctpatch/impl/poly/PocketComputerRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@

import dan200.computercraft.shared.pocket.core.PocketServerComputer;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import eu.pb4.cctpatch.impl.config.PatchConfig;
import eu.pb4.cctpatch.impl.poly.ext.TerminalExt;
import eu.pb4.cctpatch.impl.poly.render.ImageView;
import eu.pb4.cctpatch.impl.poly.textures.GuiTextures;
import eu.pb4.cctpatch.impl.poly.textures.RepeatingCanvas;
import eu.pb4.mapcanvas.api.core.DrawableCanvas;
import eu.pb4.mapcanvas.api.core.PlayerCanvas;
import eu.pb4.mapcanvas.api.utils.CanvasUtils;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
import net.minecraft.server.network.ServerPlayerEntity;
import org.apache.commons.lang3.mutable.MutableObject;

import java.nio.charset.Charset;

public class PocketComputerRenderer {
private final PocketServerComputer computer;
Expand All @@ -27,36 +36,116 @@ public void onRemoved(Entity entity) {
}

public void onRendererChanged(Entity entity) {

}

public void tick(Entity entity) {
if (entity instanceof ServerPlayerEntity player
&& ((player.getMainHandStack().getItem() instanceof PocketComputerItem
&& PocketComputerItem.getServerComputer(player.server, player.getMainHandStack()) == this.computer)
|| (player.getOffHandStack().getItem() instanceof PocketComputerItem
&& PocketComputerItem.getServerComputer(player.server, player.getOffHandStack()) == this.computer))
var mut = new MutableObject<ItemStack>();
if (PatchConfig.instance.displayPocketComputerScreenInHand && entity instanceof ServerPlayerEntity player
&& this.findStack(player, mut)
) {
if (this.canvas == null) {
this.player = player;
this.canvas = DrawableCanvas.create();
this.drawInitial();
this.canvas.addPlayer(player);

int slot;
if (mut.getValue() == player.getMainHandStack()) {
slot = player.getInventory().selectedSlot;
} else {
slot = 40; // offhand
}

player.networkHandler.sendPacket(new ScreenHandlerSlotUpdateS2CPacket(-2, 0, slot, mut.getValue()));
} else if (this.player != player) {
this.canvas.removePlayer(this.player);
this.canvas.addPlayer(player);
this.player = player;
}

var image = TerminalExt.of(this.computer).getMiniRenderer().getImage(player.getWorld().getTime());
CanvasUtils.draw(this.canvas, 0, 0, image);
this.drawUpdate();
this.canvas.sendUpdates();
} else if (this.canvas != null) {
this.canvas.destroy();
this.canvas.destroy();
this.canvas = null;
this.player = null;
}
}
public void updateValues(Entity entity) {

private boolean findStack(ServerPlayerEntity player, MutableObject<ItemStack> mut) {
if (player.getMainHandStack().getItem() instanceof PocketComputerItem
&& PocketComputerItem.getServerComputer(player.server, player.getMainHandStack()) == this.computer) {
mut.setValue(player.getMainHandStack());
return true;
} else if (player.getOffHandStack().getItem() instanceof PocketComputerItem
&& PocketComputerItem.getServerComputer(player.server, player.getOffHandStack()) == this.computer) {
mut.setValue(player.getOffHandStack());
return true;
}
return false;
}

private void drawInitial() {
var terminal = TerminalExt.of(this.computer).getMiniRenderer();
int termX = 64 - terminal.renderedWidth() / 2;
int termY = 64 - terminal.renderedHeight() / 2;
var compText = switch (this.computer.getFamily()) {
case NORMAL -> GuiTextures.COMPUTER;
case ADVANCED -> GuiTextures.ADVANCED_COMPUTER;
case COMMAND -> GuiTextures.COMMAND_COMPUTER;
};

new ImageView(
termX, termY - compText.top().getHeight(),
new RepeatingCanvas(compText.top(), terminal.renderedWidth(), compText.top().getHeight())
).render(this.canvas, 0, 0, 0);

new ImageView(
termX, termY + terminal.renderedHeight(),
new RepeatingCanvas(compText.bottom(), terminal.renderedWidth(), compText.bottom().getHeight())
).render(this.canvas, 0, 0, 0);

new ImageView(
termX - compText.leftSide().getWidth(), termY,
new RepeatingCanvas(compText.leftSide(), compText.leftSide().getWidth(), terminal.renderedHeight())
).render(this.canvas, 0, 0, 0);


new ImageView(
termX + terminal.renderedWidth(), termY,
new RepeatingCanvas(compText.rightSide(), compText.rightSide().getWidth(), terminal.renderedHeight())
).render(this.canvas, 0, 0, 0);

new ImageView(termX - compText.leftTop().getWidth(), termY - compText.leftTop().getHeight(), compText.leftTop())
.render(this.canvas, 0, 0, 0);
new ImageView(termX + terminal.renderedWidth(), termY - compText.rightTop().getHeight(), compText.rightTop())
.render(this.canvas, 0, 0, 0);

new ImageView(termX - compText.leftBottom().getWidth(), termY + terminal.renderedHeight(), compText.leftBottom())
.render(this.canvas, 0, 0, 0);
new ImageView(termX + terminal.renderedWidth(), termY + terminal.renderedHeight(), compText.rightBottom())
.render(this.canvas, 0, 0, 0);
}

private void drawUpdate() {
var image = TerminalExt.of(this.computer).getMiniRenderer().getImage(player.getWorld().getTime());
CanvasUtils.draw(this.canvas, (128 - image.getWidth()) / 2, (128 - image.getHeight()) / 2, image);
}

public void updateValues(Entity entity) {
if (PatchConfig.instance.displayPocketComputerScreenInHand && entity instanceof ServerPlayerEntity player
&& ((player.getMainHandStack().getItem() instanceof PocketComputerItem
&& PocketComputerItem.getServerComputer(player.server, player.getMainHandStack()) == this.computer)
|| (player.getOffHandStack().getItem() instanceof PocketComputerItem
&& PocketComputerItem.getServerComputer(player.server, player.getOffHandStack()) == this.computer))
) {
if (this.canvas == null) {
this.player = player;
this.canvas = DrawableCanvas.create();
this.drawInitial();
this.canvas.addPlayer(player);
}
}
}

public int id() {
Expand Down
Loading

0 comments on commit 15c14b6

Please sign in to comment.