+ Redstone input [WIP]
This commit is contained in:
parent
63ba61a138
commit
18b2aa5ac3
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
153
src/main/java/net/montoyo/wd/client/JSQueryDispatcher.java
Normal file
153
src/main/java/net/montoyo/wd/client/JSQueryDispatcher.java
Normal 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 + "}");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
19
src/main/java/net/montoyo/wd/core/IScreenQueryHandler.java
Normal file
19
src/main/java/net/montoyo/wd/core/IScreenQueryHandler.java
Normal 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);
|
||||
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,6 @@
|
|||
<img src="webdisplays.png" />
|
||||
</td>
|
||||
</tr>
|
||||
</div>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"textures": {
|
||||
"layer0": "webdisplays:items/upgrade"
|
||||
"layer0": "webdisplays:items/upgrade",
|
||||
"layer1": "webdisplays:items/input"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"textures": {
|
||||
"layer0": "webdisplays:items/upgrade"
|
||||
"layer0": "webdisplays:items/upgrade",
|
||||
"layer1": "webdisplays:items/output"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user