diff --git a/build.gradle b/build.gradle index 8d79c7c..7b9db99 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ apply plugin: 'net.minecraftforge.gradle.forge' //Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. -version = "1.12.2-1.0" +version = "1.12.2-1.1" group = "net.montoyo.wd" // http://maven.apache.org/guides/mini/guide-naming-conventions.html archivesBaseName = "webdisplays" @@ -21,7 +21,7 @@ compileJava { } minecraft { - version = "1.12.2-14.23.2.2611" + version = "1.12.2-14.23.5.2768" runDir = "run" // the mappings can be changed at any time, and must be in the following format. diff --git a/libs/mcef-1.12.2-0.9-api.jar b/libs/mcef-1.12.2-0.9-api.jar deleted file mode 100644 index b2bea17..0000000 Binary files a/libs/mcef-1.12.2-0.9-api.jar and /dev/null differ diff --git a/libs/mcef-1.12.2-1.11-api.jar b/libs/mcef-1.12.2-1.11-api.jar new file mode 100644 index 0000000..7f268f4 Binary files /dev/null and b/libs/mcef-1.12.2-1.11-api.jar differ diff --git a/src/main/java/net/montoyo/wd/SharedProxy.java b/src/main/java/net/montoyo/wd/SharedProxy.java index 9ed2b70..1200cc5 100644 --- a/src/main/java/net/montoyo/wd/SharedProxy.java +++ b/src/main/java/net/montoyo/wd/SharedProxy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd; @@ -11,7 +11,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.fml.server.FMLServerHandler; -import net.montoyo.mcef.utilities.Log; +import net.montoyo.wd.utilities.Log; import net.montoyo.wd.core.HasAdvancement; import net.montoyo.wd.core.JSServerRequest; import net.montoyo.wd.data.GuiData; diff --git a/src/main/java/net/montoyo/wd/WebDisplays.java b/src/main/java/net/montoyo/wd/WebDisplays.java index 85e3251..4aae24a 100644 --- a/src/main/java/net/montoyo/wd/WebDisplays.java +++ b/src/main/java/net/montoyo/wd/WebDisplays.java @@ -1,12 +1,14 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd; +import com.google.gson.Gson; import net.minecraft.advancements.Advancement; import net.minecraft.advancements.CriteriaTriggers; import net.minecraft.block.Block; +import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.SoundEvents; @@ -17,10 +19,13 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundEvent; +import net.minecraft.util.text.TextFormatting; import net.minecraftforge.client.event.ClientChatEvent; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.capabilities.CapabilityManager; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; +import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.event.ServerChatEvent; import net.minecraftforge.event.entity.item.ItemTossEvent; @@ -52,10 +57,10 @@ import java.net.URL; import java.util.Arrays; import java.util.UUID; -@Mod(modid = "webdisplays", version = WebDisplays.MOD_VERSION, dependencies = "required-after:mcef;after:opencomputers;") +@Mod(modid = "webdisplays", version = WebDisplays.MOD_VERSION, dependencies = "required-after:mcef@[1.0,2.0);after:opencomputers;after:computercraft;") public class WebDisplays { - public static final String MOD_VERSION = "1.0"; + public static final String MOD_VERSION = "1.1"; @Mod.Instance(owner = "webdisplays") public static WebDisplays INSTANCE; @@ -67,6 +72,8 @@ public class WebDisplays { public static WDCreativeTab CREATIVE_TAB; public static final ResourceLocation ADV_PAD_BREAK = new ResourceLocation("webdisplays", "webdisplays/pad_break"); public static final String BLACKLIST_URL = "mod://webdisplays/blacklisted.html"; + public static final Gson GSON = new Gson(); + public static final ResourceLocation CAPABILITY = new ResourceLocation("webdisplays", "customdatacap"); //Blocks public BlockScreen blockScreen; @@ -105,6 +112,7 @@ public class WebDisplays { private int lastPadId = 0; public boolean doHardRecipe; private boolean hasOC; + private boolean hasCC; private String[] blacklist; public boolean disableOwnershipThief; public double unloadDistance2; @@ -241,13 +249,14 @@ public class WebDisplays { @Mod.EventHandler public void onInit(FMLInitializationEvent ev) { //Register tile entities - GameRegistry.registerTileEntity(TileEntityScreen.class, "webdisplays:screen"); + GameRegistry.registerTileEntity(TileEntityScreen.class, new ResourceLocation("webdisplays", "screen")); for(DefaultPeripheral dp: DefaultPeripheral.values()) { if(dp.getTEClass() != null) - GameRegistry.registerTileEntity(dp.getTEClass(), "webdisplays:" + dp.getName()); + GameRegistry.registerTileEntity(dp.getTEClass(), new ResourceLocation("webdisplays", dp.getName())); } //Other things + CapabilityManager.INSTANCE.register(IWDDCapability.class, new WDDCapability.Storage(), new WDDCapability.Factory()); PROXY.init(); NET_HANDLER = NetworkRegistry.INSTANCE.newSimpleChannel("webdisplays"); Messages.registerAll(NET_HANDLER); @@ -257,6 +266,17 @@ public class WebDisplays { public void onPostInit(FMLPostInitializationEvent ev) { PROXY.postInit(); hasOC = Loader.isModLoaded("opencomputers"); + hasCC = Loader.isModLoaded("computercraft"); + + if(hasCC) { + try { + //We have to do this because the "register" method might be stripped out if CC isn't loaded + CCPeripheralProvider.class.getMethod("register").invoke(null); + } catch(Throwable t) { + Log.error("ComputerCraft was found, but WebDisplays wasn't able to register its CC Interface Peripheral"); + t.printStackTrace(); + } + } } @SubscribeEvent @@ -371,8 +391,20 @@ public class WebDisplays { @SubscribeEvent public void onLogIn(PlayerEvent.PlayerLoggedInEvent ev) { - if(!ev.player.world.isRemote && ev.player instanceof EntityPlayerMP) + if(!ev.player.world.isRemote && ev.player instanceof EntityPlayerMP) { WebDisplays.NET_HANDLER.sendTo(new CMessageServerInfo(miniservPort), (EntityPlayerMP) ev.player); + IWDDCapability cap = ev.player.getCapability(WDDCapability.INSTANCE, null); + + if(cap == null) + Log.warning("Player %s (%s) has null IWDDCapability!", ev.player.getName(), ev.player.getGameProfile().getId().toString()); + else if(cap.isFirstRun()) { + Util.toast(ev.player, TextFormatting.LIGHT_PURPLE, "welcome1"); + Util.toast(ev.player, TextFormatting.LIGHT_PURPLE, "welcome2"); + Util.toast(ev.player, TextFormatting.LIGHT_PURPLE, "welcome3"); + + cap.clearFirstRun(); + } + } } @SubscribeEvent @@ -381,6 +413,30 @@ public class WebDisplays { Server.getInstance().getClientManager().revokeClientKey(ev.player.getGameProfile().getId()); } + @SubscribeEvent + public void attachEntityCaps(AttachCapabilitiesEvent ev) { + if(ev.getObject() instanceof EntityPlayer) + ev.addCapability(CAPABILITY, new WDDCapability.Provider()); + } + + @SubscribeEvent + public void onPlayerClone(net.minecraftforge.event.entity.player.PlayerEvent.Clone ev) { + IWDDCapability src = ev.getOriginal().getCapability(WDDCapability.INSTANCE, null); + IWDDCapability dst = ev.getEntityPlayer().getCapability(WDDCapability.INSTANCE, null); + + if(src == null) { + Log.error("src is null"); + return; + } + + if(dst == null) { + Log.error("dst is null"); + return; + } + + src.cloneTo(dst); + } + @SubscribeEvent public void onServerChat(ServerChatEvent ev) { String msg = ev.getMessage().trim().replaceAll("\\s+", " ").toLowerCase(); @@ -435,6 +491,10 @@ public class WebDisplays { return INSTANCE.hasOC; } + public static boolean isComputerCraftAvailable() { + return INSTANCE.hasCC; + } + public static boolean isSiteBlacklisted(String url) { try { URL url2 = new URL(Util.addProtocol(url)); diff --git a/src/main/java/net/montoyo/wd/block/BlockPeripheral.java b/src/main/java/net/montoyo/wd/block/BlockPeripheral.java index 8f73f98..49be3ac 100644 --- a/src/main/java/net/montoyo/wd/block/BlockPeripheral.java +++ b/src/main/java/net/montoyo/wd/block/BlockPeripheral.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd.block; @@ -33,10 +33,7 @@ import net.minecraft.world.World; import net.minecraftforge.fml.common.network.NetworkRegistry; import net.montoyo.wd.WebDisplays; import net.montoyo.wd.core.DefaultPeripheral; -import net.montoyo.wd.entity.TileEntityKeyboard; -import net.montoyo.wd.entity.TileEntityOCInterface; -import net.montoyo.wd.entity.TileEntityPeripheralBase; -import net.montoyo.wd.entity.TileEntityServer; +import net.montoyo.wd.entity.*; import net.montoyo.wd.item.ItemLinker; import net.montoyo.wd.item.ItemPeripheral; import net.montoyo.wd.net.client.CMessageCloseGui; @@ -209,8 +206,8 @@ public class BlockPeripheral extends WDBlockContainer { if(te instanceof TileEntityServer) ((TileEntityServer) te).setOwner((EntityPlayer) placer); - else if(te instanceof TileEntityOCInterface) - ((TileEntityOCInterface) te).setOwner((EntityPlayer) placer); + else if(te instanceof TileEntityInterfaceBase) + ((TileEntityInterfaceBase) te).setOwner((EntityPlayer) placer); } } diff --git a/src/main/java/net/montoyo/wd/client/ClientProxy.java b/src/main/java/net/montoyo/wd/client/ClientProxy.java index 5df3825..a9e5645 100644 --- a/src/main/java/net/montoyo/wd/client/ClientProxy.java +++ b/src/main/java/net/montoyo/wd/client/ClientProxy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd.client; @@ -15,7 +15,6 @@ import net.minecraft.client.multiplayer.ClientAdvancementManager; import net.minecraft.client.renderer.block.model.ModelResourceLocation; import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.client.resources.IResourceManager; -import net.minecraft.client.resources.IResourceManagerReloadListener; import net.minecraft.client.resources.SimpleReloadableResourceManager; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Slot; @@ -31,9 +30,10 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -import net.minecraftforge.client.GuiIngameForge; import net.minecraftforge.client.event.*; import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.client.resource.IResourceType; +import net.minecraftforge.client.resource.ISelectiveResourceReloadListener; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.fml.client.registry.ClientRegistry; @@ -65,8 +65,9 @@ import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.*; +import java.util.function.Predicate; -public class ClientProxy extends SharedProxy implements IResourceManagerReloadListener, IDisplayHandler, IJSQueryHandler { +public class ClientProxy extends SharedProxy implements ISelectiveResourceReloadListener, IDisplayHandler, IJSQueryHandler { public class PadData { @@ -126,7 +127,7 @@ public class ClientProxy extends SharedProxy implements IResourceManagerReloadLi mcef = MCEFApi.getAPI(); if(mcef != null) - mcef.registerScheme("wd", WDScheme.class, true, false, false); + mcef.registerScheme("wd", WDScheme.class, true, false, false, true, true, false, false); } @Override @@ -374,7 +375,7 @@ public class ClientProxy extends SharedProxy implements IResourceManagerReloadLi /**************************************** RESOURCE MANAGER METHODS ****************************************/ @Override - public void onResourceManagerReload(@Nonnull IResourceManager rm) { + public void onResourceManagerReload(IResourceManager resourceManager, Predicate resourcePredicate) { Log.info("Resource manager reload: clearing GUI cache..."); GuiLoader.clearCache(); } diff --git a/src/main/java/net/montoyo/wd/client/gui/GuiKeyboard.java b/src/main/java/net/montoyo/wd/client/gui/GuiKeyboard.java index cebe6fd..e4d5739 100644 --- a/src/main/java/net/montoyo/wd/client/gui/GuiKeyboard.java +++ b/src/main/java/net/montoyo/wd/client/gui/GuiKeyboard.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd.client.gui; @@ -17,10 +17,12 @@ import net.montoyo.wd.entity.TileEntityScreen; import net.montoyo.wd.net.server.SMessageScreenCtrl; import net.montoyo.wd.utilities.BlockSide; import net.montoyo.wd.utilities.Log; +import net.montoyo.wd.utilities.TypeData; import net.montoyo.wd.utilities.Util; import org.lwjgl.input.Keyboard; import java.io.*; +import java.util.ArrayList; import java.util.Map; @SideOnly(Side.CLIENT) @@ -30,8 +32,7 @@ public class GuiKeyboard extends WDScreen { private TileEntityScreen tes; private BlockSide side; - private String eventStack = ""; - private boolean lastIsType = false; + private final ArrayList evStack = new ArrayList<>(); private BlockPos kbPos; private boolean showWarning = true; @@ -119,45 +120,28 @@ public class GuiKeyboard extends WDScreen { else { char chr = Keyboard.getEventCharacter(); - if(chr == '\n' || chr == '\r' || chr == '\b') { - if(Keyboard.getEventKeyState()) { - if(lastIsType) - lastIsType = false; + if(Keyboard.getEventKeyState()) { + int kc = Keyboard.getEventKey(); - if(!eventStack.isEmpty()) - eventStack += (char) 1; - - eventStack += 'p'; - eventStack += chr; - eventStack += (char) 1; - eventStack += 'r'; - eventStack += chr; - } - } else if(chr != 0) { - if(!lastIsType) { - if(!eventStack.isEmpty()) - eventStack += (char) 1; - - eventStack += 't'; - lastIsType = true; - } - - eventStack += chr; + evStack.add(new TypeData(TypeData.Action.PRESS, kc, chr)); + evStack.add(new TypeData(TypeData.Action.RELEASE, kc, chr)); } + + if(chr != 0) + evStack.add(new TypeData(TypeData.Action.TYPE, 0, chr)); } } - if(!eventStack.isEmpty() && !syncRequested()) + if(!evStack.isEmpty() && !syncRequested()) requestSync(); } } @Override protected void sync() { - if(!eventStack.isEmpty()) { - WebDisplays.NET_HANDLER.sendToServer(SMessageScreenCtrl.type(tes, side, eventStack, kbPos)); - eventStack = ""; - lastIsType = false; + if(!evStack.isEmpty()) { + WebDisplays.NET_HANDLER.sendToServer(SMessageScreenCtrl.type(tes, side, WebDisplays.GSON.toJson(evStack), kbPos)); + evStack.clear(); } } diff --git a/src/main/java/net/montoyo/wd/client/gui/GuiMinePad.java b/src/main/java/net/montoyo/wd/client/gui/GuiMinePad.java index 3cd516e..60d4bf0 100644 --- a/src/main/java/net/montoyo/wd/client/gui/GuiMinePad.java +++ b/src/main/java/net/montoyo/wd/client/gui/GuiMinePad.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd.client.gui; @@ -82,6 +82,7 @@ public class GuiMinePad extends WDScreen { public void handleInput() { while(Keyboard.next()) { char key = Keyboard.getEventCharacter(); + int keycode = Keyboard.getEventKey(); boolean pressed = Keyboard.getEventKeyState(); if(Keyboard.getEventKey() == Keyboard.KEY_ESCAPE) { @@ -90,14 +91,12 @@ public class GuiMinePad extends WDScreen { } if(pad.view != null) { - if(key != '.' && key != ';' && key != ',') { - if(pressed) - pad.view.injectKeyPressed(key, 0); - else - pad.view.injectKeyReleased(key, 0); - } + if(pressed) + pad.view.injectKeyPressedByKeyCode(keycode, key, 0); + else + pad.view.injectKeyReleasedByKeyCode(keycode, key, 0); - if(key != Keyboard.CHAR_NONE) + if(key != 0) pad.view.injectKeyTyped(key, 0); } } diff --git a/src/main/java/net/montoyo/wd/client/gui/GuiRedstoneCtrl.java b/src/main/java/net/montoyo/wd/client/gui/GuiRedstoneCtrl.java index c814bd8..823a5eb 100644 --- a/src/main/java/net/montoyo/wd/client/gui/GuiRedstoneCtrl.java +++ b/src/main/java/net/montoyo/wd/client/gui/GuiRedstoneCtrl.java @@ -1,11 +1,13 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd.client.gui; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; +import net.montoyo.mcef.api.API; +import net.montoyo.wd.client.ClientProxy; import net.montoyo.wd.WebDisplays; import net.montoyo.wd.client.gui.controls.Button; import net.montoyo.wd.client.gui.controls.TextField; @@ -54,8 +56,10 @@ public class GuiRedstoneCtrl extends WDScreen { @GuiSubscribe public void onClick(Button.ClickEvent ev) { if(ev.getSource() == btnOk) { - String rising = Util.addProtocol(tfRisingEdge.getText()); - String falling = Util.addProtocol(tfFallingEdge.getText()); + API mcef = ((ClientProxy) WebDisplays.PROXY).getMCEF(); + + String rising = mcef.punycode(Util.addProtocol(tfRisingEdge.getText())); + String falling = mcef.punycode(Util.addProtocol(tfFallingEdge.getText())); WebDisplays.NET_HANDLER.sendToServer(new SMessageRedstoneCtrl(dimension, pos, rising, falling)); } diff --git a/src/main/java/net/montoyo/wd/client/gui/GuiSetURL2.java b/src/main/java/net/montoyo/wd/client/gui/GuiSetURL2.java index 63f31e5..0fa337f 100644 --- a/src/main/java/net/montoyo/wd/client/gui/GuiSetURL2.java +++ b/src/main/java/net/montoyo/wd/client/gui/GuiSetURL2.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd.client.gui; @@ -94,6 +94,7 @@ public class GuiSetURL2 extends WDScreen { private void validate(String url) { if(!url.isEmpty()) { url = Util.addProtocol(url); + url = ((ClientProxy) WebDisplays.PROXY).getMCEF().punycode(url); if(isPad) { WebDisplays.NET_HANDLER.sendToServer(new SMessagePadCtrl(url)); diff --git a/src/main/java/net/montoyo/wd/core/CCArguments.java b/src/main/java/net/montoyo/wd/core/CCArguments.java new file mode 100644 index 0000000..caab3ae --- /dev/null +++ b/src/main/java/net/montoyo/wd/core/CCArguments.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 BARBOTIN Nicolas + */ + +package net.montoyo.wd.core; + +import java.util.List; +import java.util.Map; + +public class CCArguments implements IComputerArgs { + + private final Object[] args; + + public CCArguments(Object[] args) { + this.args = args; + } + + @Override + public String checkString(int i) { + checkIndex(i, "string"); + + Object obj = args[i]; + if(!(obj instanceof String)) + throw typeError(i, "string", obj); + + return (String) obj; + } + + @Override + public int checkInteger(int i) { + checkIndex(i, "number"); + + Object obj = args[i]; + int ret; + + if(obj instanceof Integer) + ret = (int) obj; + else if(obj instanceof Double) + ret = ((Double) obj).intValue(); + else if(obj instanceof Float) + ret = ((Float) obj).intValue(); + else + throw typeError(i, "number", obj); + + return ret; + } + + @Override + public Map checkTable(int i) { + checkIndex(i, "table"); + + Object obj = args[i]; + if(!(obj instanceof Map)) + throw typeError(i, "table", args[i]); + + return (Map) obj; + } + + private void checkIndex(int idx, String want) { + if(idx < 0 || idx >= args.length) + typeError(idx, want, null); + } + + private static IllegalArgumentException typeError(int idx, String want, Object got) { + return new IllegalArgumentException("bad argument #" + (idx + 1) + " (" + want + " expected, got " + luaTypeName(got) + ")"); + } + + private static String luaTypeName(Object obj) { + if(obj == null) + return "nil"; + + Class cls = obj.getClass(); + if(cls == Boolean.class || cls == Boolean.TYPE) + return "boolean"; + else if(cls == Integer.class || cls == Integer.TYPE || cls == Double.class || cls == Double.TYPE || cls == Float.class || cls == Float.TYPE) + return "number"; + else if(cls == String.class) + return "string"; + else if(Map.class.isAssignableFrom(cls)) + return "table"; + else + return cls.getSimpleName(); + } + + @Override + public int count() { + return args.length; + } + +} diff --git a/src/main/java/net/montoyo/wd/core/CCPeripheralProvider.java b/src/main/java/net/montoyo/wd/core/CCPeripheralProvider.java new file mode 100644 index 0000000..fefe0ca --- /dev/null +++ b/src/main/java/net/montoyo/wd/core/CCPeripheralProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 BARBOTIN Nicolas + */ + +package net.montoyo.wd.core; + +import dan200.computercraft.api.ComputerCraftAPI; +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.api.peripheral.IPeripheralProvider; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fml.common.Optional; +import net.montoyo.wd.entity.TileEntityCCInterface; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +@Optional.Interface(iface = "dan200.computercraft.api.peripheral.IPeripheralProvider", modid = "computercraft") +public class CCPeripheralProvider implements IPeripheralProvider { + + private CCPeripheralProvider() { + } + + @Optional.Method(modid = "computercraft") + @Nullable + @Override + public IPeripheral getPeripheral(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull EnumFacing f) { + TileEntity te = world.getTileEntity(pos); + return (te instanceof TileEntityCCInterface) ? ((TileEntityCCInterface) te) : null; + } + + @Optional.Method(modid = "computercraft") + public static void register() { + ComputerCraftAPI.registerPeripheralProvider(new CCPeripheralProvider()); + } + +} diff --git a/src/main/java/net/montoyo/wd/core/DefaultPeripheral.java b/src/main/java/net/montoyo/wd/core/DefaultPeripheral.java index 13633a0..6028169 100644 --- a/src/main/java/net/montoyo/wd/core/DefaultPeripheral.java +++ b/src/main/java/net/montoyo/wd/core/DefaultPeripheral.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd.core; @@ -13,7 +13,7 @@ import javax.annotation.Nonnull; public enum DefaultPeripheral implements IStringSerializable { KEYBOARD("keyboard", "Keyboard", TileEntityKeyboard.class), //WITH FACING (< 3) - CC_INTERFACE("ccinterface", "ComputerCraft_Interface", null), + CC_INTERFACE("ccinterface", "ComputerCraft_Interface", TileEntityCCInterface.class), OC_INTERFACE("cointerface", "OpenComputers_Interface", TileEntityOCInterface.class), REMOTE_CONTROLLER("remotectrl", "Remote_Controller", TileEntityRCtrl.class), //WITHOUT FACING (>= 3) REDSTONE_CONTROLLER("redstonectrl", "Redstone_Controller", TileEntityRedCtrl.class), diff --git a/src/main/java/net/montoyo/wd/core/IComputerArgs.java b/src/main/java/net/montoyo/wd/core/IComputerArgs.java new file mode 100644 index 0000000..366e549 --- /dev/null +++ b/src/main/java/net/montoyo/wd/core/IComputerArgs.java @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2019 BARBOTIN Nicolas + */ + +package net.montoyo.wd.core; + +import java.util.Map; + +public interface IComputerArgs { + + String checkString(int i); + int checkInteger(int i); + Map checkTable(int i); + int count(); + +} diff --git a/src/main/java/net/montoyo/wd/core/IWDDCapability.java b/src/main/java/net/montoyo/wd/core/IWDDCapability.java new file mode 100644 index 0000000..de2a75e --- /dev/null +++ b/src/main/java/net/montoyo/wd/core/IWDDCapability.java @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2019 BARBOTIN Nicolas + */ + +package net.montoyo.wd.core; + +public interface IWDDCapability { + + boolean isFirstRun(); + void clearFirstRun(); + void cloneTo(IWDDCapability dst); + +} diff --git a/src/main/java/net/montoyo/wd/core/OCArguments.java b/src/main/java/net/montoyo/wd/core/OCArguments.java new file mode 100644 index 0000000..5846ba9 --- /dev/null +++ b/src/main/java/net/montoyo/wd/core/OCArguments.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 BARBOTIN Nicolas + */ + +package net.montoyo.wd.core; + +import li.cil.oc.api.machine.Arguments; +import net.minecraftforge.fml.common.Optional; + +import java.util.Map; + +@Optional.Interface(iface = "net.montoyo.wd.core.IComputerArgs", modid = "opencomputers") +public class OCArguments implements IComputerArgs { + + //Keep this as an "Object" so that it doesn't crash if OC is absent + private final Object args; + + public OCArguments(Object a) { + args = a; + } + + @Optional.Method(modid = "opencomputers") + @Override + public String checkString(int i) { + return ((Arguments) args).checkString(i); + } + + @Optional.Method(modid = "opencomputers") + @Override + public int checkInteger(int i) { + return ((Arguments) args).checkInteger(i); + } + + @Optional.Method(modid = "opencomputers") + @Override + public Map checkTable(int i) { + return ((Arguments) args).checkTable(i); + } + + @Optional.Method(modid = "opencomputers") + @Override + public int count() { + return ((Arguments) args).count(); + } + +} diff --git a/src/main/java/net/montoyo/wd/core/WDDCapability.java b/src/main/java/net/montoyo/wd/core/WDDCapability.java new file mode 100644 index 0000000..f48590e --- /dev/null +++ b/src/main/java/net/montoyo/wd/core/WDDCapability.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 BARBOTIN Nicolas + */ + +package net.montoyo.wd.core; + +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagByte; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityInject; +import net.minecraftforge.common.capabilities.ICapabilitySerializable; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.concurrent.Callable; + +public class WDDCapability implements IWDDCapability { + + @CapabilityInject(IWDDCapability.class) + public static final Capability INSTANCE = null; + + public static class Storage implements Capability.IStorage { + + @Nullable + @Override + public NBTBase writeNBT(Capability cap, IWDDCapability inst, EnumFacing side) { + NBTTagCompound tag = new NBTTagCompound(); + tag.setBoolean("FirstRun", inst.isFirstRun()); + + return tag; + } + + @Override + public void readNBT(Capability cap, IWDDCapability inst, EnumFacing side, NBTBase nbt) { + if(nbt instanceof NBTTagCompound) { + NBTTagCompound tag = (NBTTagCompound) nbt; + + if(tag.hasKey("FirstRun") && tag.getTag("FirstRun") instanceof NBTTagByte && !tag.getBoolean("FirstRun")) + inst.clearFirstRun(); + } + } + + } + + public static class Factory implements Callable { + + @Override + public IWDDCapability call() throws Exception { + return new WDDCapability(); + } + + } + + public static class Provider implements ICapabilitySerializable { + + private IWDDCapability cap = INSTANCE.getDefaultInstance(); + + @Override + public boolean hasCapability(@Nonnull Capability cap, @Nullable EnumFacing f) { + return cap == INSTANCE; + } + + @Nullable + @Override + public T getCapability(@Nonnull Capability cap, @Nullable EnumFacing f) { + return cap == INSTANCE ? INSTANCE.cast(this.cap) : null; + } + + @Override + public NBTBase serializeNBT() { + return INSTANCE.getStorage().writeNBT(INSTANCE, cap, null); + } + + @Override + public void deserializeNBT(NBTBase nbt) { + INSTANCE.getStorage().readNBT(INSTANCE, cap, null, nbt); + } + + } + + private boolean firstRun = true; + + private WDDCapability() { + } + + @Override + public boolean isFirstRun() { + return firstRun; + } + + @Override + public void clearFirstRun() { + firstRun = false; + } + + @Override + public void cloneTo(IWDDCapability dst) { + if(!isFirstRun()) + dst.clearFirstRun(); + } + +} diff --git a/src/main/java/net/montoyo/wd/entity/TileEntityCCInterface.java b/src/main/java/net/montoyo/wd/entity/TileEntityCCInterface.java new file mode 100644 index 0000000..c2ce365 --- /dev/null +++ b/src/main/java/net/montoyo/wd/entity/TileEntityCCInterface.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 BARBOTIN Nicolas + */ + +package net.montoyo.wd.entity; + +import dan200.computercraft.api.lua.ILuaContext; +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.api.peripheral.IPeripheral; +import net.minecraftforge.fml.common.Optional; +import net.montoyo.wd.core.CCArguments; +import net.montoyo.wd.core.IComputerArgs; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; + +@Optional.Interface(iface = "dan200.computercraft.api.peripheral.IPeripheral", modid = "computercraft") +public class TileEntityCCInterface extends TileEntityInterfaceBase implements IPeripheral { + + private static final String[] METHOD_NAMES; + private static final Method[] METHODS; + + static { + ArrayList names = new ArrayList<>(); + ArrayList methods = new ArrayList<>(); + Method[] src = TileEntityInterfaceBase.class.getMethods(); + + for(Method m: src) { + if(m.getAnnotation(TileEntityInterfaceBase.ComputerFunc.class) != null) { + if(m.getParameterCount() != 1 || m.getParameterTypes()[0] != IComputerArgs.class) + throw new RuntimeException("Found @ComputerFunc method with invalid arguments"); + + if(m.getReturnType() != Object[].class) + throw new RuntimeException("Found @ComputerFunc method with invalid return type"); + + names.add(m.getName()); + methods.add(m); + } + } + + METHOD_NAMES = names.toArray(new String[0]); + METHODS = methods.toArray(new Method[0]); + } + + @Nonnull + @Override + public String getType() { + return "webdisplays"; + } + + @Nonnull + @Override + public String[] getMethodNames() { + return METHOD_NAMES; + } + + @Optional.Method(modid = "computercraft") + @Nullable + @Override + public Object[] callMethod(@Nonnull IComputerAccess ca, @Nonnull ILuaContext ctx, int mid, @Nonnull Object[] args) throws LuaException, InterruptedException { + try { + return (Object[]) METHODS[mid].invoke(this, new CCArguments(args)); + } catch(IllegalAccessException e) { + throw new RuntimeException(e); + } catch(InvocationTargetException e) { + if(e.getCause() instanceof IllegalArgumentException) + throw new LuaException(e.getCause().getMessage()); + else + throw new RuntimeException(e.getCause()); + } + } + + @Optional.Method(modid = "computercraft") + @Override + public boolean equals(@Nullable IPeripheral periph) { + return periph == this; + } + +} diff --git a/src/main/java/net/montoyo/wd/entity/TileEntityInterfaceBase.java b/src/main/java/net/montoyo/wd/entity/TileEntityInterfaceBase.java new file mode 100644 index 0000000..cbf3b25 --- /dev/null +++ b/src/main/java/net/montoyo/wd/entity/TileEntityInterfaceBase.java @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2019 BARBOTIN Nicolas + */ + +package net.montoyo.wd.entity; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; +import net.montoyo.wd.WebDisplays; +import net.montoyo.wd.core.IComputerArgs; +import net.montoyo.wd.core.IUpgrade; +import net.montoyo.wd.core.ScreenRights; +import net.montoyo.wd.net.client.CMessageScreenUpdate; +import net.montoyo.wd.utilities.*; + +import javax.annotation.Nonnull; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.ArrayList; +import java.util.Map; + +public abstract class TileEntityInterfaceBase extends TileEntityPeripheralBase { + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface ComputerFunc {} + + private NameUUIDPair owner; + private static final Object[] TRUE = new Object[] { true }; + private static final Object[] FALSE = new Object[] { false }; + + @Override + public void readFromNBT(NBTTagCompound tag) { + super.readFromNBT(tag); + owner = Util.readOwnerFromNBT(tag); + } + + @Override + @Nonnull + public NBTTagCompound writeToNBT(NBTTagCompound tag) { + super.writeToNBT(tag); + return Util.writeOwnerToNBT(tag, owner); + } + + public void setOwner(EntityPlayer ep) { + owner = new NameUUIDPair(ep.getGameProfile()); + markDirty(); + } + + @ComputerFunc + public Object[] isLinked(IComputerArgs args) { + return new Object[] { isLinked() }; + } + + @ComputerFunc + public Object[] isScreenChunkLoaded(IComputerArgs args) { + return new Object[] { isScreenChunkLoaded() }; + } + + @ComputerFunc + public Object[] getScreenPos(IComputerArgs args) { + return isLinked() ? new Object[] { screenPos.x, screenPos.y, screenPos.z } : null; + } + + @ComputerFunc + public Object[] getScreenSide(IComputerArgs args) { + return isLinked() ? new Object[] { screenSide.toString().toLowerCase() } : null; + } + + @ComputerFunc + public Object[] getOwner(IComputerArgs args) { + if(owner == null) + return null; + else + return new Object[] { owner.name, owner.uuid.toString() }; + } + + @ComputerFunc + public Object[] can(IComputerArgs args) { + String what = args.checkString(0).toLowerCase(); + int right; + switch(what) { + case "click": + case "type": + right = ScreenRights.CLICK; + break; + + case "seturl": + case "js": + case "javascript": + case "runjs": + right = ScreenRights.CHANGE_URL; + break; + + case "setresolution": + case "setrotation": + right = ScreenRights.CHANGE_RESOLUTION; + break; + + default: + throw new IllegalArgumentException("invalid right name"); + } + + TileEntityScreen tes = getConnectedScreenEx(); + if(owner == null || tes == null) + return null; + else + return ((tes.getScreen(screenSide).rightsFor(owner.uuid) & right) == 0) ? FALSE : TRUE; + } + + @ComputerFunc + public Object[] hasUpgrade(IComputerArgs args) { + String name = args.checkString(0); + + TileEntityScreen tes = getConnectedScreenEx(); + if(owner == null || tes == null) + return null; + else + return tes.getScreen(screenSide).upgrades.stream().anyMatch(is -> ((IUpgrade) is.getItem()).getJSName(is).equalsIgnoreCase(name)) ? TRUE : FALSE; + } + + @ComputerFunc + public Object[] getSize(IComputerArgs args) { + TileEntityScreen tes = getConnectedScreenEx(); + + if(owner == null || tes == null) + return null; + else { + Vector2i sz = tes.getScreen(screenSide).size; + return new Object[] { sz.x, sz.y }; + } + } + + @ComputerFunc + public Object[] getResolution(IComputerArgs args) { + TileEntityScreen tes = getConnectedScreenEx(); + + if(owner == null || tes == null) + return null; + else { + Vector2i res = tes.getScreen(screenSide).resolution; + return new Object[] { res.x, res.y }; + } + } + + @ComputerFunc + public Object[] getRotation(IComputerArgs args) { + TileEntityScreen tes = getConnectedScreenEx(); + + if(owner == null || tes == null) + return null; + else + return new Object[] { tes.getScreen(screenSide).rotation.getAngleAsInt() }; + } + + @ComputerFunc + public Object[] getURL(IComputerArgs args) { + TileEntityScreen tes = getConnectedScreenEx(); + + if(owner == null || tes == null) + return null; + else + return new Object[] { tes.getScreen(screenSide).url }; + } + + private static Object[] err(String str) { + return new Object[] { false, str }; + } + + @ComputerFunc + public Object[] click(IComputerArgs args) { + int x = args.checkInteger(0); + int y = args.checkInteger(1); + String action = "click"; + if(args.count() > 2) + action = args.checkString(2).toLowerCase(); + + int actionId; + switch(action) { + case "click": + actionId = CMessageScreenUpdate.MOUSE_CLICK; + break; + + case "up": + case "release": + actionId = CMessageScreenUpdate.MOUSE_UP; + break; + + case "down": + case "press": + actionId = CMessageScreenUpdate.MOUSE_DOWN; + break; + + case "move": + actionId = CMessageScreenUpdate.MOUSE_MOVE; + break; + + default: + throw new IllegalArgumentException("bad action name"); + } + + TileEntityScreen scr = getConnectedScreenEx(); + + if(owner == null || scr == null) + return err("notlinked"); + else { + TileEntityScreen.Screen scrscr = scr.getScreen(screenSide); + + if((scrscr.rightsFor(owner.uuid) & ScreenRights.CLICK) == 0) + return err("restrictions"); + else { + switch(scrscr.rotation) { + case ROT_90: + y = scrscr.resolution.y - y; + break; + + case ROT_180: + x = scrscr.resolution.x - x; + y = scrscr.resolution.y - y; + break; + + case ROT_270: + x = scrscr.resolution.x - x; + break; + + default: + break; + } + + if(scrscr.rotation.isVertical) + scr.clickUnsafe(screenSide, actionId, y, x); + else + scr.clickUnsafe(screenSide, actionId, x, y); + + return TRUE; + } + } + } + + private Object[] realType(String what) { + TileEntityScreen scr = getConnectedScreenEx(); + + if(owner == null || scr == null) + return err("notlinked"); + else if((scr.getScreen(screenSide).rightsFor(owner.uuid) & ScreenRights.CLICK) == 0) + return err("restrictions"); + else { + scr.type(screenSide, what, null); + return TRUE; + } + } + + @ComputerFunc + public Object[] type(IComputerArgs args) { + String text = args.checkString(0); + if(text.length() > 64) + return err("toolong"); + + if(text.indexOf((char) 1) >= 0) + return err("badchar"); + + return realType("t" + text); + } + + @ComputerFunc + public Object[] typeAdvanced(IComputerArgs args) { + ArrayList data = new ArrayList<>(); + Map map = args.checkTable(0); + int maxEvents = 0; + + for(Object o: map.values()) { + if(!(o instanceof Map)) + return err("badinput"); + + if(++maxEvents >= 16) + return err("toomany"); + + Map event = (Map) o; + Object action = event.get("action"); + Object chr = event.get("char"); + int code = 0; + + if(!(action instanceof String)) + return err("badaction"); + + if(!(chr instanceof String)) + return err("badchar"); + + String strAction = (String) action; + String strChr = (String) chr; + TypeData.Action dataAction; + + if(strAction.equalsIgnoreCase("press")) + dataAction = TypeData.Action.PRESS; + else if(strAction.equalsIgnoreCase("release")) + dataAction = TypeData.Action.RELEASE; + else if(strAction.equalsIgnoreCase("type")) + dataAction = TypeData.Action.TYPE; + else + return err("unknownaction"); + + if(strChr.isEmpty()) + return err("emptychar"); + + if(dataAction != TypeData.Action.TYPE) { + Object oCode = event.get("code"); + if(!(oCode instanceof Double)) + return err("badcode"); + + code = ((Double) oCode).intValue(); + } + + data.add(new TypeData(dataAction, code, strChr.charAt(0))); + } + + return realType(WebDisplays.GSON.toJson(data)); + } + + @ComputerFunc + public Object[] setURL(IComputerArgs args) { + String url = args.checkString(0); + TileEntityScreen scr = getConnectedScreenEx(); + + if(owner == null || scr == null) + return err("notlinked"); + else if((scr.getScreen(screenSide).rightsFor(owner.uuid) & ScreenRights.CHANGE_URL) == 0) + return err("restrictions"); + else { + scr.setScreenURL(screenSide, url); + return TRUE; + } + } + + @ComputerFunc + public Object[] setResolution(IComputerArgs args) { + int rx = args.checkInteger(0); + int ry = args.checkInteger(1); + TileEntityScreen scr = getConnectedScreenEx(); + + if(owner == null || scr == null) + return err("notlinked"); + else if((scr.getScreen(screenSide).rightsFor(owner.uuid) & ScreenRights.CHANGE_RESOLUTION) == 0) + return err("restrictions"); + else { + scr.setResolution(screenSide, new Vector2i(rx, ry)); + return TRUE; + } + } + + @ComputerFunc + public Object[] setRotation(IComputerArgs args) { + int rot = args.checkInteger(0); + if(rot < 0) { + int toAdd = (rot / -360) + 1; + rot += toAdd * 360; + } + + rot /= 90; + rot &= 3; + + TileEntityScreen scr = getConnectedScreenEx(); + + if(owner == null || scr == null) + return err("notlinked"); + else if((scr.getScreen(screenSide).rightsFor(owner.uuid) & ScreenRights.CHANGE_RESOLUTION) == 0) + return err("restrictions"); + else { + scr.setRotation(screenSide, Rotation.values()[rot]); + return TRUE; + } + } + + @ComputerFunc + public Object[] runJS(IComputerArgs args) { + String code = args.checkString(0); + TileEntityScreen scr = getConnectedScreenEx(); + + if(owner == null || scr == null) + return err("notlinked"); + else if((scr.getScreen(screenSide).rightsFor(owner.uuid) & ScreenRights.CHANGE_URL) == 0) + return err("restrictions"); + else { + scr.evalJS(screenSide, code); + return TRUE; + } + } + + @ComputerFunc + public Object[] unlink(IComputerArgs args) { + if(isLinked()) { + screenPos = null; + screenSide = null; + markDirty(); + } + + return null; + } + +} diff --git a/src/main/java/net/montoyo/wd/entity/TileEntityOCInterface.java b/src/main/java/net/montoyo/wd/entity/TileEntityOCInterface.java index 4f463fc..70f235b 100644 --- a/src/main/java/net/montoyo/wd/entity/TileEntityOCInterface.java +++ b/src/main/java/net/montoyo/wd/entity/TileEntityOCInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd.entity; @@ -8,43 +8,11 @@ import li.cil.oc.api.machine.Arguments; import li.cil.oc.api.machine.Callback; import li.cil.oc.api.machine.Context; import li.cil.oc.api.network.SimpleComponent; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.fml.common.Optional; -import net.montoyo.wd.core.IUpgrade; -import net.montoyo.wd.core.ScreenRights; -import net.montoyo.wd.net.client.CMessageScreenUpdate; -import net.montoyo.wd.utilities.NameUUIDPair; -import net.montoyo.wd.utilities.Rotation; -import net.montoyo.wd.utilities.Util; -import net.montoyo.wd.utilities.Vector2i; - -import javax.annotation.Nonnull; +import net.montoyo.wd.core.OCArguments; @Optional.Interface(iface = "li.cil.oc.api.network.SimpleComponent", modid = "opencomputers") -public class TileEntityOCInterface extends TileEntityPeripheralBase implements SimpleComponent { - - private NameUUIDPair owner; - private static final Object[] TRUE = new Object[] { true }; - private static final Object[] FALSE = new Object[] { false }; - - @Override - public void readFromNBT(NBTTagCompound tag) { - super.readFromNBT(tag); - owner = Util.readOwnerFromNBT(tag); - } - - @Override - @Nonnull - public NBTTagCompound writeToNBT(NBTTagCompound tag) { - super.writeToNBT(tag); - return Util.writeOwnerToNBT(tag, owner); - } - - public void setOwner(EntityPlayer ep) { - owner = new NameUUIDPair(ep.getGameProfile()); - markDirty(); - } +public class TileEntityOCInterface extends TileEntityInterfaceBase implements SimpleComponent { @Override public String getComponentName() { @@ -54,335 +22,115 @@ public class TileEntityOCInterface extends TileEntityPeripheralBase implements S @Callback @Optional.Method(modid = "opencomputers") public Object[] isLinked(Context ctx, Arguments args) { - return new Object[] { isLinked() }; + return isLinked(new OCArguments(args)); } @Callback @Optional.Method(modid = "opencomputers") public Object[] isScreenChunkLoaded(Context ctx, Arguments args) { - return new Object[] { isScreenChunkLoaded() }; + return isScreenChunkLoaded(new OCArguments(args)); } @Callback @Optional.Method(modid = "opencomputers") public Object[] getScreenPos(Context ctx, Arguments args) { - return isLinked() ? new Object[] { screenPos.x, screenPos.y, screenPos.z } : null; + return getScreenPos(new OCArguments(args)); } @Callback @Optional.Method(modid = "opencomputers") public Object[] getScreenSide(Context ctx, Arguments args) { - return isLinked() ? new Object[] { screenSide.toString().toLowerCase() } : null; + return getScreenSide(new OCArguments(args)); } @Callback @Optional.Method(modid = "opencomputers") public Object[] getOwner(Context ctx, Arguments args) { - if(owner == null) - return null; - else - return new Object[] { owner.name, owner.uuid.toString() }; + return getOwner(new OCArguments(args)); } @Callback @Optional.Method(modid = "opencomputers") public Object[] can(Context ctx, Arguments args) { - String what = args.checkString(0).toLowerCase(); - int right; - switch(what) { - case "click": - case "type": - right = ScreenRights.CLICK; - break; - - case "seturl": - case "js": - case "javascript": - case "runjs": - right = ScreenRights.CHANGE_URL; - break; - - case "setresolution": - case "setrotation": - right = ScreenRights.CHANGE_RESOLUTION; - break; - - default: - throw new IllegalArgumentException("invalid right name"); - } - - TileEntityScreen tes = getConnectedScreenEx(); - if(owner == null || tes == null) - return null; - else - return ((tes.getScreen(screenSide).rightsFor(owner.uuid) & right) == 0) ? FALSE : TRUE; + return can(new OCArguments(args)); } @Callback @Optional.Method(modid = "opencomputers") public Object[] hasUpgrade(Context ctx, Arguments args) { - String name = args.checkString(0); - - TileEntityScreen tes = getConnectedScreenEx(); - if(owner == null || tes == null) - return null; - else - return tes.getScreen(screenSide).upgrades.stream().anyMatch(is -> ((IUpgrade) is.getItem()).getJSName(is).equalsIgnoreCase(name)) ? TRUE : FALSE; + return hasUpgrade(new OCArguments(args)); } @Callback @Optional.Method(modid = "opencomputers") public Object[] getSize(Context ctx, Arguments args) { - TileEntityScreen tes = getConnectedScreenEx(); - - if(owner == null || tes == null) - return null; - else { - Vector2i sz = tes.getScreen(screenSide).size; - return new Object[] { sz.x, sz.y }; - } + return getSize(new OCArguments(args)); } @Callback @Optional.Method(modid = "opencomputers") public Object[] getResolution(Context ctx, Arguments args) { - TileEntityScreen tes = getConnectedScreenEx(); - - if(owner == null || tes == null) - return null; - else { - Vector2i res = tes.getScreen(screenSide).resolution; - return new Object[] { res.x, res.y }; - } + return getResolution(new OCArguments(args)); } @Callback @Optional.Method(modid = "opencomputers") public Object[] getRotation(Context ctx, Arguments args) { - TileEntityScreen tes = getConnectedScreenEx(); - - if(owner == null || tes == null) - return null; - else - return new Object[] { tes.getScreen(screenSide).rotation.getAngleAsInt() }; + return getRotation(new OCArguments(args)); } @Callback @Optional.Method(modid = "opencomputers") public Object[] getURL(Context ctx, Arguments args) { - TileEntityScreen tes = getConnectedScreenEx(); - - if(owner == null || tes == null) - return null; - else - return new Object[] { tes.getScreen(screenSide).url }; - } - - private static Object[] err(String str) { - return new Object[] { false, str }; + return getURL(new OCArguments(args)); } @Callback(limit = 4) @Optional.Method(modid = "opencomputers") public Object[] click(Context ctx, Arguments args) { - int x = args.checkInteger(0); - int y = args.checkInteger(1); - String action = "click"; - if(args.count() > 2) - action = args.checkString(2).toLowerCase(); - - int actionId; - switch(action) { - case "click": - actionId = CMessageScreenUpdate.MOUSE_CLICK; - break; - - case "up": - case "release": - actionId = CMessageScreenUpdate.MOUSE_UP; - break; - - case "down": - case "press": - actionId = CMessageScreenUpdate.MOUSE_DOWN; - break; - - case "move": - actionId = CMessageScreenUpdate.MOUSE_MOVE; - break; - - default: - throw new IllegalArgumentException("bad action name"); - } - - TileEntityScreen scr = getConnectedScreenEx(); - - if(owner == null || scr == null) - return err("notlinked"); - else { - TileEntityScreen.Screen scrscr = scr.getScreen(screenSide); - - if((scrscr.rightsFor(owner.uuid) & ScreenRights.CLICK) == 0) - return err("restrictions"); - else { - switch(scrscr.rotation) { - case ROT_90: - y = scrscr.resolution.y - y; - break; - - case ROT_180: - x = scrscr.resolution.x - x; - y = scrscr.resolution.y - y; - break; - - case ROT_270: - x = scrscr.resolution.x - x; - break; - - default: - break; - } - - if(scrscr.rotation.isVertical) - scr.clickUnsafe(screenSide, actionId, y, x); - else - scr.clickUnsafe(screenSide, actionId, x, y); - - return TRUE; - } - } - } - - private Object[] realType(String what) { - TileEntityScreen scr = getConnectedScreenEx(); - - if(owner == null || scr == null) - return err("notlinked"); - else if((scr.getScreen(screenSide).rightsFor(owner.uuid) & ScreenRights.CLICK) == 0) - return err("restrictions"); - else { - scr.type(screenSide, what, null); - return TRUE; - } + return click(new OCArguments(args)); } @Callback(limit = 4) @Optional.Method(modid = "opencomputers") public Object[] type(Context ctx, Arguments args) { - String text = args.checkString(0); - if(text.length() > 64) - return err("toolong"); - - if(text.indexOf((char) 1) >= 0) - return err("badchar"); - - return realType("t" + text); + return type(new OCArguments(args)); } @Callback(limit = 4) @Optional.Method(modid = "opencomputers") public Object[] typeAdvanced(Context ctx, Arguments args) { - String text = args.checkString(0); - if(text.length() > 64) - return err("toolong"); - - String[] ctrl = text.split("" + ((char) 1)); - for(String c: ctrl) { - if(c.length() < 2) - return err("badformat"); - - if(c.charAt(0) != 't' && c.charAt(0) != 'p' && c.charAt(0) != 'r') - return err("badformat"); - } - - return realType(text); + return typeAdvanced(new OCArguments(args)); } @Callback(limit = 1) @Optional.Method(modid = "opencomputers") public Object[] setURL(Context ctx, Arguments args) { - String url = args.checkString(0); - TileEntityScreen scr = getConnectedScreenEx(); - - if(owner == null || scr == null) - return err("notlinked"); - else if((scr.getScreen(screenSide).rightsFor(owner.uuid) & ScreenRights.CHANGE_URL) == 0) - return err("restrictions"); - else { - scr.setScreenURL(screenSide, url); - return TRUE; - } + return setURL(new OCArguments(args)); } @Callback(limit = 1) @Optional.Method(modid = "opencomputers") public Object[] setResolution(Context ctx, Arguments args) { - int rx = args.checkInteger(0); - int ry = args.checkInteger(1); - TileEntityScreen scr = getConnectedScreenEx(); - - if(owner == null || scr == null) - return err("notlinked"); - else if((scr.getScreen(screenSide).rightsFor(owner.uuid) & ScreenRights.CHANGE_RESOLUTION) == 0) - return err("restrictions"); - else { - scr.setResolution(screenSide, new Vector2i(rx, ry)); - return TRUE; - } + return setResolution(new OCArguments(args)); } @Callback(limit = 1) @Optional.Method(modid = "opencomputers") public Object[] setRotation(Context ctx, Arguments args) { - int rot = args.checkInteger(0); - if(rot < 0) { - int toAdd = (rot / -360) + 1; - rot += toAdd * 360; - } - - rot /= 90; - rot &= 3; - - TileEntityScreen scr = getConnectedScreenEx(); - - if(owner == null || scr == null) - return err("notlinked"); - else if((scr.getScreen(screenSide).rightsFor(owner.uuid) & ScreenRights.CHANGE_RESOLUTION) == 0) - return err("restrictions"); - else { - scr.setRotation(screenSide, Rotation.values()[rot]); - return TRUE; - } + return setRotation(new OCArguments(args)); } @Callback(limit = 4) @Optional.Method(modid = "opencomputers") public Object[] runJS(Context ctx, Arguments args) { - String code = args.checkString(0); - TileEntityScreen scr = getConnectedScreenEx(); - - if(owner == null || scr == null) - return err("notlinked"); - else if((scr.getScreen(screenSide).rightsFor(owner.uuid) & ScreenRights.CHANGE_URL) == 0) - return err("restrictions"); - else { - scr.evalJS(screenSide, code); - return TRUE; - } + return runJS(new OCArguments(args)); } @Callback @Optional.Method(modid = "opencomputers") public Object[] unlink(Context ctx, Arguments args) { - if(isLinked()) { - screenPos = null; - screenSide = null; - markDirty(); - } - - return null; + return unlink(new OCArguments(args)); } - - } diff --git a/src/main/java/net/montoyo/wd/entity/TileEntityScreen.java b/src/main/java/net/montoyo/wd/entity/TileEntityScreen.java index 68cbe3e..1a5c40a 100644 --- a/src/main/java/net/montoyo/wd/entity/TileEntityScreen.java +++ b/src/main/java/net/montoyo/wd/entity/TileEntityScreen.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd.entity; @@ -759,20 +759,35 @@ public class TileEntityScreen extends TileEntity { if(world.isRemote) { if(scr.browser != null) { try { - String[] events = text.split("" + ((char) 1)); + if(text.startsWith("t")) { + for(int i = 1; i < text.length(); i++) { + char chr = text.charAt(i); + if(chr == 1) + break; - for(String ev: events) { - char action = ev.charAt(0); + scr.browser.injectKeyTyped(chr, 0); + } + } else { + TypeData[] data = WebDisplays.GSON.fromJson(text, TypeData[].class); - if(action == 'p') - scr.browser.injectKeyPressed(ev.charAt(1), 0); - else if(action == 'r') - scr.browser.injectKeyReleased(ev.charAt(1), 0); - else if(action == 't') { - for(int i = 1; i < ev.length(); i++) - scr.browser.injectKeyTyped(ev.charAt(i), 0); - } else - throw new RuntimeException("Invalid control key '" + action + '\''); + for(TypeData ev : data) { + switch(ev.getAction()) { + case PRESS: + scr.browser.injectKeyPressedByKeyCode(ev.getKeyCode(), ev.getKeyChar(), 0); + break; + + case RELEASE: + scr.browser.injectKeyReleasedByKeyCode(ev.getKeyCode(), ev.getKeyChar(), 0); + break; + + case TYPE: + scr.browser.injectKeyTyped(ev.getKeyChar(), 0); + break; + + default: + throw new RuntimeException("Invalid type action '" + ev.getAction() + '\''); + } + } } } catch(Throwable t) { Log.warningEx("Suspicious keyboard type packet received...", t); diff --git a/src/main/java/net/montoyo/wd/item/ItemPeripheral.java b/src/main/java/net/montoyo/wd/item/ItemPeripheral.java index d2aefe8..fb02149 100644 --- a/src/main/java/net/montoyo/wd/item/ItemPeripheral.java +++ b/src/main/java/net/montoyo/wd/item/ItemPeripheral.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd.item; @@ -56,8 +56,8 @@ public class ItemPeripheral extends ItemMultiTexture implements WDItem { super.addInformation(is, world, tt, ttFlags); if(is != null && tt != null) { - if(is.getMetadata() == 1) //CC Interface - tt.add("" + ChatFormatting.RED + I18n.format("webdisplays.message.missingCC")); //CC is not available for 1.12.2 + if(is.getMetadata() == 1 && !WebDisplays.isComputerCraftAvailable()) //CC Interface + tt.add("" + ChatFormatting.RED + I18n.format("webdisplays.message.missingCC")); else if(is.getMetadata() == 2 && !WebDisplays.isOpenComputersAvailable()) //OC Interface tt.add("" + ChatFormatting.RED + I18n.format("webdisplays.message.missingOC")); else if(is.getMetadata() == 11 && WebDisplays.PROXY.isMiniservDisabled()) //Server diff --git a/src/main/java/net/montoyo/wd/net/client/CMessageOpenGui.java b/src/main/java/net/montoyo/wd/net/client/CMessageOpenGui.java index a254d7b..f5af535 100644 --- a/src/main/java/net/montoyo/wd/net/client/CMessageOpenGui.java +++ b/src/main/java/net/montoyo/wd/net/client/CMessageOpenGui.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd.net.client; @@ -8,7 +8,7 @@ import io.netty.buffer.ByteBuf; import net.minecraftforge.fml.common.network.ByteBufUtils; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.relauncher.Side; -import net.montoyo.mcef.utilities.Log; +import net.montoyo.wd.utilities.Log; import net.montoyo.wd.WebDisplays; import net.montoyo.wd.data.GuiData; import net.montoyo.wd.net.Message; diff --git a/src/main/java/net/montoyo/wd/net/server/SMessageRequestTEData.java b/src/main/java/net/montoyo/wd/net/server/SMessageRequestTEData.java index e59adb8..1f16db1 100644 --- a/src/main/java/net/montoyo/wd/net/server/SMessageRequestTEData.java +++ b/src/main/java/net/montoyo/wd/net/server/SMessageRequestTEData.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 BARBOTIN Nicolas + * Copyright (C) 2019 BARBOTIN Nicolas */ package net.montoyo.wd.net.server; @@ -13,7 +13,7 @@ import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; import net.minecraftforge.fml.relauncher.Side; -import net.montoyo.mcef.utilities.Log; +import net.montoyo.wd.utilities.Log; import net.montoyo.wd.entity.TileEntityScreen; import net.montoyo.wd.net.Message; import net.montoyo.wd.utilities.Vector3i; diff --git a/src/main/java/net/montoyo/wd/utilities/TypeData.java b/src/main/java/net/montoyo/wd/utilities/TypeData.java new file mode 100644 index 0000000..70dcbbd --- /dev/null +++ b/src/main/java/net/montoyo/wd/utilities/TypeData.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 BARBOTIN Nicolas + */ + +package net.montoyo.wd.utilities; + +import com.google.gson.annotations.SerializedName; + +public class TypeData { + + public enum Action { + @SerializedName("i") + INVALID, + + @SerializedName("p") + PRESS, + + @SerializedName("r") + RELEASE, + + @SerializedName("t") + TYPE + } + + private Action a; + private int k; + private int c; + + public TypeData() { + a = Action.INVALID; + k = 0; + c = 0; + } + + public TypeData(Action action, int code, char chr) { + a = action; + k = code; + c = (int) chr; + } + + public Action getAction() { + return a; + } + + public char getKeyChar() { + return (char) c; + } + + public int getKeyCode() { + return k; + } + +} diff --git a/src/main/resources/assets/webdisplays/lang/en_us.lang b/src/main/resources/assets/webdisplays/lang/en_us.lang index f332a08..6322965 100644 --- a/src/main/resources/assets/webdisplays/lang/en_us.lang +++ b/src/main/resources/assets/webdisplays/lang/en_us.lang @@ -54,6 +54,9 @@ webdisplays.message.upgradeOk=Upgrade installed! webdisplays.message.linkAbort=Linker reset webdisplays.message.noMiniserv=Server block is disabled on this server webdisplays.message.otDisabled=Ownership thief is disabled on this server +webdisplays.message.welcome1=Thank you for installing WebDisplays! If you +webdisplays.message.welcome2=need help, hover any WD item with your mouse +webdisplays.message.welcome3=and hit F1. Have fun with the mod, - montoyo webdisplays.gui.screencfg.owner=Screen owner: webdisplays.gui.screencfg.friends=Friends: webdisplays.gui.screencfg.permissions=Permissions: diff --git a/src/main/resources/assets/webdisplays/lang/fr_fr.lang b/src/main/resources/assets/webdisplays/lang/fr_fr.lang index 30b74ec..03dc747 100644 --- a/src/main/resources/assets/webdisplays/lang/fr_fr.lang +++ b/src/main/resources/assets/webdisplays/lang/fr_fr.lang @@ -10,7 +10,7 @@ tile.webdisplays.peripheral.server.name=Serveur item.webdisplays.screencfg.name=Gestionnaire d'écran item.webdisplays.ownerthief.name=Voleur d'écran [ADMIN] item.webdisplays.linker.name=Outil d'appairage -item.webdisplays.craftcomp.name=Composant de recette +item.webdisplays.craftcomp.name=Ingrédient de recette item.webdisplays.craftcomp.stonekey.name=Touche en pierre item.webdisplays.craftcomp.upgrade.name=Amélioration vide item.webdisplays.craftcomp.peripheral.name=Base de périphérique @@ -54,6 +54,9 @@ webdisplays.message.upgradeOk=Amélioration installée ! webdisplays.message.linkAbort=Outil d'appairage remis à zéro. webdisplays.message.noMiniserv=Le bloc serveur est désactivé webdisplays.message.otDisabled=Le voleur d'écran est désactivé sur ce serveur +webdisplays.message.welcome1=Merci d'avoir installé WebDisplays ! Pour obtenir +webdisplays.message.welcome2=de l'aide sur un item, passez la souris dessus +webdisplays.message.welcome3=et appuyez sur F1. Amusez-vous bien, - montoyo webdisplays.gui.screencfg.owner=Propriétaire : webdisplays.gui.screencfg.friends=Amis : webdisplays.gui.screencfg.permissions=Permissions : diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index 56f42ab..a0f81c6 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -8,7 +8,7 @@ "url": "https://montoyo.net/wd2.php", "updateUrl": "", "authorList": [ "montoyo" ], - "credits": "Thanks to sadreminderwindows for the Chinese translation", + "credits": "Thanks to sadreminderwindows for the Chinese translation. Thanks to binary1230 for the ComputerCraft support.", "logoFile": "", "screenshots": [], "dependencies": []