From 18b2aa5ac325498c6cd3de93ac212508b6e36fa3 Mon Sep 17 00:00:00 2001 From: Nicolas BARBOTIN Date: Wed, 31 Jan 2018 04:18:48 +0100 Subject: [PATCH] + Redstone input [WIP] --- README.md | 4 + .../net/montoyo/wd/block/BlockScreen.java | 19 +++ .../net/montoyo/wd/client/ClientProxy.java | 75 ++++++++- .../montoyo/wd/client/JSQueryDispatcher.java | 153 ++++++++++++++++++ .../montoyo/wd/core/IScreenQueryHandler.java | 19 +++ .../montoyo/wd/entity/TileEntityScreen.java | 16 +- .../montoyo/wd/net/CMessageScreenUpdate.java | 40 +++-- .../net/montoyo/wd/utilities/Vector3i.java | 4 + .../assets/webdisplays/html/main.html | 2 +- .../models/item/upgrade_redinput.json | 3 +- .../models/item/upgrade_redoutput.json | 3 +- 11 files changed, 319 insertions(+), 19 deletions(-) create mode 100644 src/main/java/net/montoyo/wd/client/JSQueryDispatcher.java create mode 100644 src/main/java/net/montoyo/wd/core/IScreenQueryHandler.java diff --git a/README.md b/README.md index 312bdfd..c2d442b 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ This is the unfinished port of the WebDisplays mod for Minecraft 1.12.2. The text below is my "TODO" list. ### Missing features +* Crafts: upgrades, laser pointer, peripherals * Screen upgrade: redstone input * Screen upgrade: redstone output * ~~Peripheral: ComputerCraft interface~~ (CC not up to date) @@ -10,6 +11,7 @@ This is the unfinished port of the WebDisplays mod for Minecraft 1.12.2. The tex * Read config (see "Config elements" below) ### TODO +* REDSTONE INPUT OPTIMIZATIONssss!!! * Achievements (minePad 2 and all that stuff) * Top/bottom screen orientation * GuiSetURL2 missing buttons @@ -23,6 +25,8 @@ This is the unfinished port of the WebDisplays mod for Minecraft 1.12.2. The tex * Enhance crafts * Enhance models * minePad item texture seems to be transparent in some corners... +* Change mod name to WD2 +* Put a limit on screen resolution ### Config elements * Site blacklist diff --git a/src/main/java/net/montoyo/wd/block/BlockScreen.java b/src/main/java/net/montoyo/wd/block/BlockScreen.java index 354a43f..ba43c7c 100644 --- a/src/main/java/net/montoyo/wd/block/BlockScreen.java +++ b/src/main/java/net/montoyo/wd/block/BlockScreen.java @@ -4,6 +4,7 @@ package net.montoyo.wd.block; +import net.minecraft.block.Block; import net.minecraft.block.material.EnumPushReaction; import net.minecraft.block.material.Material; import net.minecraft.block.properties.IProperty; @@ -29,6 +30,7 @@ import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.IUnlistedProperty; import net.minecraftforge.common.property.Properties; import net.montoyo.wd.WebDisplays; +import net.montoyo.wd.core.DefaultUpgrade; import net.montoyo.wd.core.ScreenRights; import net.montoyo.wd.core.IUpgrade; import net.montoyo.wd.data.SetURLData; @@ -188,6 +190,23 @@ public class BlockScreen extends WDBlockContainer { return true; } + @Override + public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos source) { + if(block != this && !world.isRemote) { //I'm worried this could blow up, hopefully this won't create a bug or something + for(BlockSide side: BlockSide.values()) { + Vector3i vec = new Vector3i(pos); + Multiblock.findOrigin(world, vec, side, null); + + TileEntityScreen tes = (TileEntityScreen) world.getTileEntity(vec.toBlock()); + if(tes != null && tes.hasUpgrade(side, WebDisplays.INSTANCE.itemUpgrade, DefaultUpgrade.REDSTONE_INPUT.ordinal())) { + EnumFacing facing = EnumFacing.VALUES[side.reverse().ordinal()]; //Opposite face + vec.sub(pos.getX(), pos.getY(), pos.getZ()).neg(); + tes.updateJSRedstone(side, new Vector2i(vec.dot(side.right), vec.dot(side.up)), world.getRedstonePower(pos, facing)); + } + } + } + } + public static boolean hit2pixels(BlockSide side, BlockPos bpos, Vector3i pos, TileEntityScreen.Screen scr, float hitX, float hitY, float hitZ, Vector2i dst) { if(side.right.x < 0) hitX -= 1.f; diff --git a/src/main/java/net/montoyo/wd/client/ClientProxy.java b/src/main/java/net/montoyo/wd/client/ClientProxy.java index 5535f3c..c2a6a0b 100644 --- a/src/main/java/net/montoyo/wd/client/ClientProxy.java +++ b/src/main/java/net/montoyo/wd/client/ClientProxy.java @@ -31,9 +31,7 @@ import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.fml.client.registry.ClientRegistry; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; -import net.montoyo.mcef.api.IBrowser; -import net.montoyo.mcef.api.IDisplayHandler; -import net.montoyo.mcef.api.MCEFApi; +import net.montoyo.mcef.api.*; import net.montoyo.wd.SharedProxy; import net.montoyo.wd.WebDisplays; import net.montoyo.wd.block.BlockScreen; @@ -53,7 +51,7 @@ import net.montoyo.wd.utilities.*; import java.util.ArrayList; import java.util.HashMap; -public class ClientProxy extends SharedProxy implements IResourceManagerReloadListener, IDisplayHandler { +public class ClientProxy extends SharedProxy implements IResourceManagerReloadListener, IDisplayHandler, IJSQueryHandler { public class PadData { @@ -75,6 +73,7 @@ public class ClientProxy extends SharedProxy implements IResourceManagerReloadLi private ArrayList modelBakers = new ArrayList<>(); private net.montoyo.mcef.api.API mcef; private MinePadRenderer minePadRenderer; + private JSQueryDispatcher jsDispatcher; private LaserPointerRenderer laserPointerRenderer; //Laser pointer @@ -106,6 +105,7 @@ public class ClientProxy extends SharedProxy implements IResourceManagerReloadLi public void init() { ClientRegistry.bindTileEntitySpecialRenderer(TileEntityScreen.class, new ScreenRenderer()); mcef = MCEFApi.getAPI(); + jsDispatcher = new JSQueryDispatcher(this); minePadRenderer = new MinePadRenderer(); laserPointerRenderer = new LaserPointerRenderer(); } @@ -118,6 +118,7 @@ public class ClientProxy extends SharedProxy implements IResourceManagerReloadLi throw new RuntimeException("MCEF is missing"); mcef.registerDisplayHandler(this); + mcef.registerJSQueryHandler(this); } @Override @@ -239,6 +240,46 @@ public class ClientProxy extends SharedProxy implements IResourceManagerReloadLi public void onStatusMessage(IBrowser browser, String value) { } + /**************************************** JS HANDLER METHODS ****************************************/ + + @Override + public boolean handleQuery(IBrowser browser, long queryId, String query, boolean persistent, IJSQueryCallback cb) { + if(browser != null && persistent && query != null && cb != null) { + query = query.toLowerCase(); + + if(query.startsWith("webdisplays_")) { + query = query.substring(12); + + String args; + int parenthesis = query.indexOf('('); + if(parenthesis < 0) + args = null; + else { + if(query.indexOf(')') != query.length() - 1) { + cb.failure(400, "Malformed request"); + return true; + } + + args = query.substring(parenthesis + 1, query.length() - 1); + query = query.substring(0, parenthesis); + } + + if(jsDispatcher.canHandleQuery(query)) + jsDispatcher.enqueueQuery(browser, query, args, cb); + else + cb.failure(404, "Unknown WebDisplays query"); + + return true; + } + } + + return false; + } + + @Override + public void cancelQuery(IBrowser browser, long queryId) { + } + /**************************************** EVENT METHODS ****************************************/ @SubscribeEvent @@ -368,6 +409,9 @@ public class ClientProxy extends SharedProxy implements IResourceManagerReloadLi if(!raycastHit) deselectScreen(); + + //Handle JS queries + jsDispatcher.handleQueries(); } } @@ -484,4 +528,27 @@ public class ClientProxy extends SharedProxy implements IResourceManagerReloadLi return mcef; } + public static final class ScreenSidePair { + + public TileEntityScreen tes; + public BlockSide side; + + } + + public boolean findScreenFromBrowser(IBrowser browser, ScreenSidePair pair) { + for(TileEntityScreen tes: screenTracking) { + for(int i = 0; i < tes.screenCount(); i++) { + TileEntityScreen.Screen scr = tes.getScreen(i); + + if(scr.browser == browser) { + pair.tes = tes; + pair.side = scr.side; + return true; + } + } + } + + return false; + } + } diff --git a/src/main/java/net/montoyo/wd/client/JSQueryDispatcher.java b/src/main/java/net/montoyo/wd/client/JSQueryDispatcher.java new file mode 100644 index 0000000..a91cb2c --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/JSQueryDispatcher.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client; + +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import net.montoyo.mcef.api.IBrowser; +import net.montoyo.mcef.api.IJSQueryCallback; +import net.montoyo.wd.core.IScreenQueryHandler; +import net.montoyo.wd.utilities.Log; +import net.montoyo.wd.utilities.Vector2i; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; + +@SideOnly(Side.CLIENT) +public final class JSQueryDispatcher { + + private static final class QueryData { + + private final IBrowser browser; + private final String query; + private final String args; + private final IJSQueryCallback callback; + + private QueryData(IBrowser b, String q, String a, IJSQueryCallback cb) { + browser = b; + query = q; + args = a; + callback = cb; + } + + } + + private final ClientProxy proxy; + private final ArrayDeque queue = new ArrayDeque<>(); + private final ClientProxy.ScreenSidePair lookupResult = new ClientProxy.ScreenSidePair(); + private final HashMap handlers = new HashMap<>(); + + public JSQueryDispatcher(ClientProxy proxy) { + this.proxy = proxy; + registerDefaults(); + } + + public void enqueueQuery(IBrowser b, String q, String a, IJSQueryCallback cb) { + synchronized(queue) { + queue.offer(new QueryData(b, q, a, cb)); + } + } + + public void handleQueries() { + while(true) { + QueryData next; + synchronized(queue) { + next = queue.poll(); + } + + if(next == null) + break; + + if(proxy.findScreenFromBrowser(next.browser, lookupResult)) { + Object[] args = (next.args == null) ? new Object[0] : parseArgs(next.args); + + if(args == null) + next.callback.failure(400, "Malformed request parameters"); + else { + try { + handlers.get(next.query).handleQuery(next.callback, lookupResult.tes, lookupResult.side, args); + } catch(Throwable t) { + Log.warningEx("Could not execute JS query %s(%s)", t, next.query, (next.args == null) ? "" : next.args); + } + } + } else + next.callback.failure(403, "A screen is required"); + } + } + + public boolean canHandleQuery(String q) { + return handlers.containsKey(q); + } + + private static Object[] parseArgs(String args) { + ArrayList array = new ArrayList<>(); + int lastIdx = 0; + boolean inString = false; + boolean escape = false; + boolean hadString = false; + + for(int i = 0; i < args.length(); i++) { + char chr = args.charAt(i); + + if(inString) { + if(escape) + escape = false; + else { + if(chr == '\"') + inString = false; + else if(chr == '\\') + escape = true; + } + } else if(chr == '\"') { + if(hadString) + return null; + + inString = true; + hadString = true; + } else if(chr == ',') { + array.add(args.substring(lastIdx, i).trim()); + lastIdx = i + 1; + hadString = false; + } + } + + if(inString) + return null; //Non terminated string + + array.add(args.substring(lastIdx).trim()); + Object[] ret = new Object[array.size()]; + + for(int i = 0; i < ret.length; i++) { + String str = array.get(i); + if(str.isEmpty()) + return null; //Nah... + + if(str.charAt(0) == '\"') //String + ret[i] = str.substring(1, str.length() - 1); + else { + try { + ret[i] = Double.parseDouble(str); + } catch(NumberFormatException ex) { + return null; + } + } + } + + return ret; + } + + public void register(String query, IScreenQueryHandler handler) { + handlers.put(query.toLowerCase(), handler); + } + + private void registerDefaults() { + register("getSize", (cb, tes, side, args) -> { + Vector2i size = tes.getScreen(side).size; + cb.success("{\"x\":" + size.x + ",\"y\":" + size.y + "}"); + }); + } + +} diff --git a/src/main/java/net/montoyo/wd/core/IScreenQueryHandler.java b/src/main/java/net/montoyo/wd/core/IScreenQueryHandler.java new file mode 100644 index 0000000..9ffece1 --- /dev/null +++ b/src/main/java/net/montoyo/wd/core/IScreenQueryHandler.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.core; + +import net.montoyo.mcef.api.IJSQueryCallback; +import net.montoyo.wd.entity.TileEntityScreen; +import net.montoyo.wd.utilities.BlockSide; + +import javax.annotation.Nonnull; + +public interface IScreenQueryHandler { + + //args is an array of Doubles or Strings + //The screen DOES exist, so scr.getScreen(side) is never null + void handleQuery(@Nonnull IJSQueryCallback cb, @Nonnull TileEntityScreen scr, @Nonnull BlockSide side, @Nonnull Object[] args); + +} diff --git a/src/main/java/net/montoyo/wd/entity/TileEntityScreen.java b/src/main/java/net/montoyo/wd/entity/TileEntityScreen.java index 9a5867e..1137a63 100644 --- a/src/main/java/net/montoyo/wd/entity/TileEntityScreen.java +++ b/src/main/java/net/montoyo/wd/entity/TileEntityScreen.java @@ -29,9 +29,7 @@ import net.montoyo.wd.data.ScreenConfigData; import net.montoyo.wd.net.CMessageAddScreen; import net.montoyo.wd.net.CMessageScreenUpdate; import net.montoyo.wd.net.SMessageRequestTEData; -import net.montoyo.wd.net.SMessageScreenCtrl; import net.montoyo.wd.utilities.*; -import org.lwjgl.Sys; import javax.annotation.Nullable; import java.util.*; @@ -419,6 +417,20 @@ public class TileEntityScreen extends TileEntity { } } + public void updateJSRedstone(BlockSide side, Vector2i vec, int redstoneLevel) { + Screen scr = getScreen(side); + if(scr == null) { + Log.error("Called updateJSRedstone on non-existing side %s", side.toString()); + return; + } + + if(world.isRemote) { + if(scr.browser != null) + scr.browser.runJS("if(typeof webdisplaysRedstoneCallback == \"function\") webdisplaysRedstoneCallback(" + vec.x + ", " + vec.y + ", " + redstoneLevel + ");", ""); + } else if(scr.upgrades.stream().anyMatch((is) -> is.getItem() == WebDisplays.INSTANCE.itemUpgrade && is.getMetadata() == DefaultUpgrade.REDSTONE_INPUT.ordinal())) + WebDisplays.NET_HANDLER.sendToAllAround(CMessageScreenUpdate.jsRedstone(this, side, vec, redstoneLevel), point()); + } + @Override public void onLoad() { if(world.isRemote) { diff --git a/src/main/java/net/montoyo/wd/net/CMessageScreenUpdate.java b/src/main/java/net/montoyo/wd/net/CMessageScreenUpdate.java index 1a9218d..c5c0248 100644 --- a/src/main/java/net/montoyo/wd/net/CMessageScreenUpdate.java +++ b/src/main/java/net/montoyo/wd/net/CMessageScreenUpdate.java @@ -30,6 +30,7 @@ public class CMessageScreenUpdate implements IMessage, Runnable { public static final int UPDATE_MOUSE = 3; public static final int UPDATE_TYPE = 4; public static final int UPDATE_UPGRADES = 5; + public static final int UPDATE_JS_REDSTONE = 6; public static final int MOUSE_CLICK = 0; public static final int MOUSE_UP = 1; @@ -40,11 +41,11 @@ public class CMessageScreenUpdate implements IMessage, Runnable { private BlockSide side; private int action; private String url; - private Vector2i resolution; + private Vector2i vec2i; private int mouseEvent; - private Vector2i mouse; private String text; private ItemStack[] upgrades; + private int redstoneLevel; public CMessageScreenUpdate() { } @@ -64,7 +65,7 @@ public class CMessageScreenUpdate implements IMessage, Runnable { ret.pos = new Vector3i(tes.getPos()); ret.side = side; ret.action = UPDATE_RESOLUTION; - ret.resolution = res; + ret.vec2i = res; return ret; } @@ -75,7 +76,7 @@ public class CMessageScreenUpdate implements IMessage, Runnable { ret.side = side; ret.action = UPDATE_MOUSE; ret.mouseEvent = mouseEvent; - ret.mouse = pos; + ret.vec2i = pos; return ret; } @@ -111,6 +112,17 @@ public class CMessageScreenUpdate implements IMessage, Runnable { return ret; } + public static CMessageScreenUpdate jsRedstone(TileEntityScreen tes, BlockSide side, Vector2i vec, int level) { + CMessageScreenUpdate ret = new CMessageScreenUpdate(); + ret.pos = new Vector3i(tes.getPos()); + ret.side = side; + ret.action = UPDATE_JS_REDSTONE; + ret.vec2i = vec; + ret.redstoneLevel = level; + + return ret; + } + @Override public void fromBytes(ByteBuf buf) { pos = new Vector3i(buf); @@ -123,9 +135,9 @@ public class CMessageScreenUpdate implements IMessage, Runnable { mouseEvent = buf.readByte(); if(mouseEvent != MOUSE_UP) - mouse = new Vector2i(buf); + vec2i = new Vector2i(buf); } else if(action == UPDATE_RESOLUTION) - resolution = new Vector2i(buf); + vec2i = new Vector2i(buf); else if(action == UPDATE_TYPE) text = ByteBufUtils.readUTF8String(buf); else if(action == UPDATE_UPGRADES) { @@ -133,6 +145,9 @@ public class CMessageScreenUpdate implements IMessage, Runnable { for(int i = 0; i < upgrades.length; i++) upgrades[i] = ByteBufUtils.readItemStack(buf); + } else if(action == UPDATE_JS_REDSTONE) { + vec2i = new Vector2i(buf); + redstoneLevel = buf.readByte(); } } @@ -148,9 +163,9 @@ public class CMessageScreenUpdate implements IMessage, Runnable { buf.writeByte(mouseEvent); if(mouseEvent != MOUSE_UP) - mouse.writeTo(buf); + vec2i.writeTo(buf); } else if(action == UPDATE_RESOLUTION) - resolution.writeTo(buf); + vec2i.writeTo(buf); else if(action == UPDATE_TYPE) ByteBufUtils.writeUTF8String(buf, text); else if(action == UPDATE_UPGRADES) { @@ -158,6 +173,9 @@ public class CMessageScreenUpdate implements IMessage, Runnable { for(ItemStack is: upgrades) ByteBufUtils.writeItemStack(buf, is); + } else if(action == UPDATE_JS_REDSTONE) { + vec2i.writeTo(buf); + buf.writeByte(redstoneLevel); } } @@ -179,15 +197,17 @@ public class CMessageScreenUpdate implements IMessage, Runnable { if(action == UPDATE_URL) tes.setScreenURL(side, url); else if(action == UPDATE_MOUSE) - tes.handleMouseEvent(side, mouseEvent, mouse); + tes.handleMouseEvent(side, mouseEvent, vec2i); else if(action == UPDATE_DELETE) tes.removeScreen(side); else if(action == UPDATE_RESOLUTION) - tes.setResolution(side, resolution); + tes.setResolution(side, vec2i); else if(action == UPDATE_TYPE) tes.type(side, text, null); else if(action == UPDATE_UPGRADES) tes.updateUpgrades(side, upgrades); + else if(action == UPDATE_JS_REDSTONE) + tes.updateJSRedstone(side, vec2i, redstoneLevel); else Log.warning("===> TODO"); //TODO } diff --git a/src/main/java/net/montoyo/wd/utilities/Vector3i.java b/src/main/java/net/montoyo/wd/utilities/Vector3i.java index fa9084c..75eea3e 100644 --- a/src/main/java/net/montoyo/wd/utilities/Vector3i.java +++ b/src/main/java/net/montoyo/wd/utilities/Vector3i.java @@ -199,6 +199,10 @@ public final class Vector3i { return this; } + public int dot(Vector3i vec) { + return x * vec.x + y * vec.y + z * vec.z; + } + public Vector3f toFloat() { return new Vector3f((float) x, (float) y, (float) z); } diff --git a/src/main/resources/assets/webdisplays/html/main.html b/src/main/resources/assets/webdisplays/html/main.html index 4a3edb9..cb233e0 100644 --- a/src/main/resources/assets/webdisplays/html/main.html +++ b/src/main/resources/assets/webdisplays/html/main.html @@ -77,6 +77,6 @@ - + diff --git a/src/main/resources/assets/webdisplays/models/item/upgrade_redinput.json b/src/main/resources/assets/webdisplays/models/item/upgrade_redinput.json index 23fa0d0..b869d83 100644 --- a/src/main/resources/assets/webdisplays/models/item/upgrade_redinput.json +++ b/src/main/resources/assets/webdisplays/models/item/upgrade_redinput.json @@ -1,6 +1,7 @@ { "parent": "item/generated", "textures": { - "layer0": "webdisplays:items/upgrade" + "layer0": "webdisplays:items/upgrade", + "layer1": "webdisplays:items/input" } } diff --git a/src/main/resources/assets/webdisplays/models/item/upgrade_redoutput.json b/src/main/resources/assets/webdisplays/models/item/upgrade_redoutput.json index 23fa0d0..e9d9ebd 100644 --- a/src/main/resources/assets/webdisplays/models/item/upgrade_redoutput.json +++ b/src/main/resources/assets/webdisplays/models/item/upgrade_redoutput.json @@ -1,6 +1,7 @@ { "parent": "item/generated", "textures": { - "layer0": "webdisplays:items/upgrade" + "layer0": "webdisplays:items/upgrade", + "layer1": "webdisplays:items/output" } }