+ Redstone input [WIP]

This commit is contained in:
Nicolas BARBOTIN 2018-01-31 04:18:48 +01:00
parent 63ba61a138
commit 18b2aa5ac3
11 changed files with 319 additions and 19 deletions

View File

@ -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

View File

@ -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;

View File

@ -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<ResourceModelPair> 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;
}
}

View File

@ -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<QueryData> queue = new ArrayDeque<>();
private final ClientProxy.ScreenSidePair lookupResult = new ClientProxy.ScreenSidePair();
private final HashMap<String, IScreenQueryHandler> 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<String> 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 + "}");
});
}
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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);
}

View File

@ -77,6 +77,6 @@
<img src="webdisplays.png" />
</td>
</tr>
</div>
</table>
</body>
</html>

View File

@ -1,6 +1,7 @@
{
"parent": "item/generated",
"textures": {
"layer0": "webdisplays:items/upgrade"
"layer0": "webdisplays:items/upgrade",
"layer1": "webdisplays:items/input"
}
}

View File

@ -1,6 +1,7 @@
{
"parent": "item/generated",
"textures": {
"layer0": "webdisplays:items/upgrade"
"layer0": "webdisplays:items/upgrade",
"layer1": "webdisplays:items/output"
}
}