commit 2d297478880725d8a65add6933d58d1ffe20a417 Author: Nicolas BARBOTIN Date: Fri Jan 26 18:03:02 2018 +0100 + Added base files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..23d710b --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# eclipse +bin +*.launch +.settings +.metadata +.classpath +.project + +# idea +out +*.ipr +*.iws +*.iml +.idea + +# gradle +build +.gradle + +# other +eclipse +run +/_OLD diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..62faee9 --- /dev/null +++ b/build.gradle @@ -0,0 +1,56 @@ +buildscript { + repositories { + jcenter() + maven { url = "http://files.minecraftforge.net/maven" } + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' + } +} +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" +group = "net.montoyo.wd" // http://maven.apache.org/guides/mini/guide-naming-conventions.html +archivesBaseName = "webdisplays" + +sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. +compileJava { + sourceCompatibility = targetCompatibility = '1.8' +} + +minecraft { + version = "1.12.2-14.23.1.2555" + runDir = "run" + + // the mappings can be changed at any time, and must be in the following format. + // snapshot_YYYYMMDD snapshot are built nightly. + // stable_# stables are built at the discretion of the MCP team. + // Use non-default mappings at your own risk. they may not always work. + // simply re-run your setup task after changing the mappings to update your workspace. + mappings = "snapshot_20171003" + // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. +} + +dependencies { +} + +processResources { + // this will ensure that this task is redone when the versions change. + inputs.property "version", project.version + inputs.property "mcversion", project.minecraft.version + + // replace stuff in mcmod.info, nothing else + from(sourceSets.main.resources.srcDirs) { + include 'mcmod.info' + + // replace version and mcversion + expand 'version':project.version, 'mcversion':project.minecraft.version + } + + // copy everything else except the mcmod.info + from(sourceSets.main.resources.srcDirs) { + exclude 'mcmod.info' + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..e9b9fd5 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,3 @@ +# Sets default memory used for gradle commands. Can be overridden by user or command line properties. +# This is required to provide enough memory for the Minecraft decompilation process. +org.gradle.jvmargs=-Xmx3G diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..30d399d Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e18cba7 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Sep 14 12:28:28 PDT 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-bin.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..91a7e26 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/libs/mcef-1.12.2-0.9-api.jar b/libs/mcef-1.12.2-0.9-api.jar new file mode 100644 index 0000000..476f59c Binary files /dev/null and b/libs/mcef-1.12.2-0.9-api.jar differ diff --git a/src/main/java/net/montoyo/wd/SharedProxy.java b/src/main/java/net/montoyo/wd/SharedProxy.java new file mode 100644 index 0000000..df92b3b --- /dev/null +++ b/src/main/java/net/montoyo/wd/SharedProxy.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd; + +import com.mojang.authlib.GameProfile; +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.data.GuiData; +import net.montoyo.wd.entity.TileEntityScreen; +import net.montoyo.wd.utilities.BlockSide; +import net.montoyo.wd.utilities.NameUUIDPair; +import net.montoyo.wd.utilities.Vector2i; +import net.montoyo.wd.utilities.Vector3i; + +public class SharedProxy { + + public static final int CURRENT_DIMENSION = Integer.MAX_VALUE; + + public void preInit() { + } + + public void init() { + } + + public void postInit() { + } + + public World getWorld(int dim) { + if(dim == CURRENT_DIMENSION) + throw new RuntimeException("Current dimension not available server side..."); + + return DimensionManager.getWorld(dim); + } + + public void enqueue(Runnable r) { + FMLServerHandler.instance().getServer().addScheduledTask(r); + } + + public void displayGui(GuiData data) { + Log.error("Called SharedProxy.displayGui() on server side..."); + } + + public void trackScreen(TileEntityScreen tes, boolean track) { + } + + public void onAutocompleteResult(NameUUIDPair pairs[]) { + } + + public GameProfile[] getOnlineGameProfiles() { + return FMLServerHandler.instance().getServer().getOnlinePlayerProfiles(); + } + + public void screenUpdateResolutionInGui(Vector3i pos, BlockSide side, Vector2i res) { + } + + public void displaySetPadURLGui(String padURL) { + Log.error("Called SharedProxy.displaySetPadURLGui() on server side..."); + } + + public void openMinePadGui(int padId) { + Log.error("Called SharedProxy.openMinePadGui() on server side..."); + } + +} diff --git a/src/main/java/net/montoyo/wd/WebDisplays.java b/src/main/java/net/montoyo/wd/WebDisplays.java new file mode 100644 index 0000000..e1cb854 --- /dev/null +++ b/src/main/java/net/montoyo/wd/WebDisplays.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd; + +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.SidedProxy; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.network.NetworkRegistry; +import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper; +import net.minecraftforge.fml.common.registry.GameRegistry; +import net.montoyo.wd.block.BlockKeyboardRight; +import net.montoyo.wd.block.BlockPeripheral; +import net.montoyo.wd.block.BlockScreen; +import net.montoyo.wd.core.DefaultPeripheral; +import net.montoyo.wd.core.WDCreativeTab; +import net.montoyo.wd.entity.TileEntityScreen; +import net.montoyo.wd.item.ItemLinker; +import net.montoyo.wd.item.ItemMinePad2; +import net.montoyo.wd.item.ItemOwnershipThief; +import net.montoyo.wd.item.ItemScreenConfigurator; +import net.montoyo.wd.net.Messages; +import net.montoyo.wd.utilities.Log; +import net.montoyo.wd.utilities.Util; + +import java.io.*; + +@Mod(modid = "webdisplays", version = WebDisplays.MOD_VERSION, dependencies = "required-after:mcef;") +public class WebDisplays { + + public static final String MOD_VERSION = "1.0"; + + @Mod.Instance(owner = "webdisplays") + public static WebDisplays INSTANCE; + + @SidedProxy(serverSide = "net.montoyo.wd.SharedProxy", clientSide = "net.montoyo.wd.client.ClientProxy") + public static SharedProxy PROXY; + + public static SimpleNetworkWrapper NET_HANDLER; + public static WDCreativeTab CREATIVE_TAB; + + //Blocks + public BlockScreen blockScreen; + public BlockPeripheral blockPeripheral; + public BlockKeyboardRight blockKbRight; + + //Items + public ItemScreenConfigurator itemScreenCfg; + public ItemOwnershipThief itemOwnerThief; + public ItemLinker itemLinker; + public Item itemStoneKey; + public ItemMinePad2 itemMinePad; + + //Sounds + public SoundEvent soundTyping; + + //Config + public static final double PAD_RATIO = 59.0 / 30.0; + public String homePage = "https://google.com"; //TODO: Read from config + public double padResX; + public double padResY; + private int lastPadId = 0; + + @Mod.EventHandler + public void onPreInit(FMLPreInitializationEvent ev) { + CREATIVE_TAB = new WDCreativeTab(); + + //TODO: Read configuration + final int padHeight = 480; + padResY = (double) padHeight; + padResX = padResY * PAD_RATIO; + + //Init blocks + blockScreen = new BlockScreen(); + blockScreen.makeItemBlock(); + + blockPeripheral = new BlockPeripheral(); + blockPeripheral.makeItemBlock(); + + blockKbRight = new BlockKeyboardRight(); + + //Init items + itemScreenCfg = new ItemScreenConfigurator(); + itemOwnerThief = new ItemOwnershipThief(); + itemLinker = new ItemLinker(); + itemMinePad = new ItemMinePad2(); + + itemStoneKey = new Item(); + itemStoneKey.setCreativeTab(CREATIVE_TAB); + itemStoneKey.setUnlocalizedName("webdisplays.stonekey"); + itemStoneKey.setRegistryName("stonekey"); + + PROXY.preInit(); + MinecraftForge.EVENT_BUS.register(this); + } + + @Mod.EventHandler + public void onInit(FMLInitializationEvent ev) { + //Register tile entities + GameRegistry.registerTileEntity(TileEntityScreen.class, "webdisplays:screen"); + for(DefaultPeripheral dp: DefaultPeripheral.values()) { + if(dp.getTEClass() != null) + GameRegistry.registerTileEntity(dp.getTEClass(), "webdisplays:" + dp.getName()); + } + + //Other things + PROXY.init(); + NET_HANDLER = NetworkRegistry.INSTANCE.newSimpleChannel("webdisplays"); + Messages.registerAll(NET_HANDLER); + } + + @Mod.EventHandler + public void onPostInit(FMLPostInitializationEvent ev) { + PROXY.postInit(); + } + + @SubscribeEvent + public void onRegisterBlocks(RegistryEvent.Register ev) { + ev.getRegistry().registerAll(blockScreen, blockPeripheral, blockKbRight); + } + + @SubscribeEvent + public void onRegisterItems(RegistryEvent.Register ev) { + ev.getRegistry().registerAll(blockScreen.getItem(), blockPeripheral.getItem()); + ev.getRegistry().registerAll(itemScreenCfg, itemOwnerThief, itemLinker, itemStoneKey, itemMinePad); + } + + @SubscribeEvent + public void onRegisterSounds(RegistryEvent.Register ev) { + soundTyping = new SoundEvent(new ResourceLocation("webdisplays", "keyboardType")); + soundTyping.setRegistryName(soundTyping.getSoundName()); + ev.getRegistry().register(soundTyping); + } + + @SubscribeEvent + public void onWorldLoad(WorldEvent.Load ev) { + if(ev.getWorld().isRemote || ev.getWorld().provider.getDimension() != 0) + return; + + File f = new File(ev.getWorld().getSaveHandler().getWorldDirectory(), "wd_next.txt"); + + if(f.exists()) { + try { + BufferedReader br = new BufferedReader(new FileReader(f)); + String idx = br.readLine(); + Util.silentClose(br); + + if(idx == null) + throw new RuntimeException("Seems like the file is empty (1)"); + + idx = idx.trim(); + if(idx.isEmpty()) + throw new RuntimeException("Seems like the file is empty (2)"); + + lastPadId = Integer.parseInt(idx); //This will throw NumberFormatException if it goes wrong + } catch(Throwable t) { + Log.warningEx("Could not read last minePad ID from %s. I'm afraid this might break all minePads.", t, f.getAbsolutePath()); + } + } + } + + @SubscribeEvent + public void onWorldSave(WorldEvent.Save ev) { + if(ev.getWorld().isRemote || ev.getWorld().provider.getDimension() != 0) + return; + + File f = new File(ev.getWorld().getSaveHandler().getWorldDirectory(), "wd_next.txt"); + + try { + BufferedWriter bw = new BufferedWriter(new FileWriter(f)); + bw.write("" + lastPadId + "\n"); + Util.silentClose(bw); + } catch(Throwable t) { + Log.warningEx("Could not save last minePad ID (%d) to %s. I'm afraid this might break all minePads.", t, lastPadId, f.getAbsolutePath()); + } + } + + public static int getNextAvailablePadID() { + return INSTANCE.lastPadId++; + } + +} diff --git a/src/main/java/net/montoyo/wd/block/BlockKeyboardRight.java b/src/main/java/net/montoyo/wd/block/BlockKeyboardRight.java new file mode 100644 index 0000000..24b4c2d --- /dev/null +++ b/src/main/java/net/montoyo/wd/block/BlockKeyboardRight.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +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; +import net.minecraft.block.properties.PropertyInteger; +import net.minecraft.block.state.BlockStateContainer; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.Explosion; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.montoyo.wd.WebDisplays; +import net.montoyo.wd.core.DefaultPeripheral; +import net.montoyo.wd.core.IPeripheral; +import net.montoyo.wd.entity.TileEntityKeyboard; +import net.montoyo.wd.item.ItemLinker; +import net.montoyo.wd.utilities.BlockSide; +import net.montoyo.wd.utilities.Vector3i; + +import java.util.Random; + +public class BlockKeyboardRight extends Block implements IPeripheral { + + public static final PropertyInteger facing = PropertyInteger.create("facing", 0, 3); + private static final IProperty[] properties = new IProperty[] { facing }; + public static final AxisAlignedBB KEYBOARD_AABB = new AxisAlignedBB(0.0, 0.0, 0.0, 1.0, 1.0 / 16.0, 1.0); + + public BlockKeyboardRight() { + super(Material.ROCK); + setHardness(1.5f); + setResistance(10.f); + setUnlocalizedName("webdisplays.peripheral.keyboard"); + setRegistryName("keyboard"); + fullBlock = false; + } + + @Override + protected BlockStateContainer createBlockState() { + return new BlockStateContainer(this, properties); + } + + @Override + public int quantityDropped(Random random) { + return 0; + } + + @Override + public boolean isFullCube(IBlockState state) { + return false; + } + + @Override + public boolean isFullBlock(IBlockState state) { + return false; + } + + @Override + public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { + return false; + } + + @Override + public boolean isOpaqueCube(IBlockState state) { + return false; + } + + @Override + public boolean doesSideBlockRendering(IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing face) { + return false; + } + + @Override + public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos) { + return KEYBOARD_AABB; + } + + @Override + public IBlockState getStateFromMeta(int meta) { + return getDefaultState().withProperty(facing, meta); + } + + @Override + public int getMetaFromState(IBlockState state) { + return state.getValue(facing); + } + + @Override + public ItemStack getPickBlock(IBlockState state, RayTraceResult target, World world, BlockPos pos, EntityPlayer player) { + return new ItemStack(WebDisplays.INSTANCE.blockPeripheral, 1, 0); + } + + TileEntityKeyboard getTileEntity(World world, BlockPos pos) { + for(EnumFacing nf: EnumFacing.HORIZONTALS) { + BlockPos np = pos.add(nf.getDirectionVec()); + IBlockState ns = world.getBlockState(np); + + if(ns.getBlock() instanceof BlockPeripheral && ns.getValue(BlockPeripheral.type) == DefaultPeripheral.KEYBOARD) { + TileEntity te = world.getTileEntity(np); + if(te != null && te instanceof TileEntityKeyboard) + return (TileEntityKeyboard) te; + + break; + } + } + + return null; + } + + @Override + public boolean connect(World world, BlockPos pos, IBlockState state, Vector3i scrPos, BlockSide scrSide) { + TileEntityKeyboard keyboard = getTileEntity(world, pos); + return keyboard != null && keyboard.connect(world, pos, state, scrPos, scrSide); + } + + @Override + public EnumPushReaction getMobilityFlag(IBlockState state) { + return EnumPushReaction.IGNORE; + } + + public static boolean checkNeighborhood(IBlockAccess world, BlockPos bp, BlockPos ignore) { + for(EnumFacing neighbor: EnumFacing.HORIZONTALS) { + BlockPos np = bp.add(neighbor.getDirectionVec()); + + if(ignore == null || !np.equals(ignore)) { + IBlockState state = world.getBlockState(np); + + if(state.getBlock() instanceof BlockPeripheral) { + if(state.getValue(BlockPeripheral.type) == DefaultPeripheral.KEYBOARD) + return false; + } else if(state.getBlock() instanceof BlockKeyboardRight) + return false; + } + } + + return true; + } + + public void removeLeftPiece(World world, BlockPos pos, boolean dropItem) { + for(EnumFacing nf: EnumFacing.HORIZONTALS) { + BlockPos np = pos.add(nf.getDirectionVec()); + IBlockState ns = world.getBlockState(np); + + if(ns.getBlock() instanceof BlockPeripheral && ns.getValue(BlockPeripheral.type) == DefaultPeripheral.KEYBOARD) { + if(dropItem) + ns.getBlock().dropBlockAsItem(world, np, ns, 0); + + world.setBlockToAir(np); + break; + } + } + } + + @Override + public void neighborChanged(IBlockState state, World world, BlockPos pos, Block neighborType, BlockPos neighbor) { + if(world.isRemote) + return; + + if(neighbor.getX() == pos.getX() && neighbor.getY() == pos.getY() - 1 && neighbor.getZ() == pos.getZ() && world.isAirBlock(neighbor)) { + removeLeftPiece(world, pos, true); + world.setBlockToAir(pos); + } + } + + @Override + public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, EntityPlayer ply, boolean willHarvest) { + if(!world.isRemote) + removeLeftPiece(world, pos, !ply.isCreative()); + + return super.removedByPlayer(state, world, pos, ply, willHarvest); + } + + @Override + public void onBlockDestroyedByExplosion(World world, BlockPos pos, Explosion explosionIn) { + if(!world.isRemote) + removeLeftPiece(world, pos, true); + } + + @Override + public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) { + double rpos = (entity.posY - ((double) pos.getY())) * 16.0; + if(!world.isRemote && rpos >= 1.0 && rpos <= 2.0 && Math.random() < 0.25) { + TileEntityKeyboard tek = getTileEntity(world, pos); + + if(tek != null) + tek.simulateCat(entity); + } + } + + @Override + public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) { + if(player.isSneaking()) + return false; + + if(player.getHeldItem(hand).getItem() instanceof ItemLinker) + return false; + + TileEntityKeyboard tek = getTileEntity(world, pos); + if(tek != null) + return tek.onRightClick(player, hand, BlockSide.values()[facing.ordinal()]); + + return false; + } + +} diff --git a/src/main/java/net/montoyo/wd/block/BlockPeripheral.java b/src/main/java/net/montoyo/wd/block/BlockPeripheral.java new file mode 100644 index 0000000..0a39030 --- /dev/null +++ b/src/main/java/net/montoyo/wd/block/BlockPeripheral.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +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; +import net.minecraft.block.properties.PropertyEnum; +import net.minecraft.block.properties.PropertyInteger; +import net.minecraft.block.state.BlockStateContainer; +import net.minecraft.block.state.IBlockState; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumBlockRenderType; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.NonNullList; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3i; +import net.minecraft.world.Explosion; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.montoyo.wd.WebDisplays; +import net.montoyo.wd.core.DefaultPeripheral; +import net.montoyo.wd.entity.TileEntityKeyboard; +import net.montoyo.wd.entity.TileEntityPeripheralBase; +import net.montoyo.wd.item.ItemLinker; +import net.montoyo.wd.item.ItemPeripheral; +import net.montoyo.wd.utilities.BlockSide; +import net.montoyo.wd.utilities.Log; + +import javax.annotation.Nullable; + +public class BlockPeripheral extends WDBlockContainer { + + public static final PropertyEnum type = PropertyEnum.create("type", DefaultPeripheral.class); + public static final PropertyInteger facing = PropertyInteger.create("facing", 0, 3); + private static final IProperty[] properties = new IProperty[] { type, facing }; + + public BlockPeripheral() { + super(Material.ROCK); + setHardness(1.5f); + setResistance(10.f); + setCreativeTab(WebDisplays.CREATIVE_TAB); + setName("peripheral"); + } + + @Override + protected ItemBlock createItemBlock() { + return new ItemPeripheral(this); + } + + @Override + protected BlockStateContainer createBlockState() { + return new BlockStateContainer(this, properties); + } + + @Override + public IBlockState getStateForPlacement(World world, BlockPos pos, EnumFacing rrezozei, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer, EnumHand hand) { + int rot = MathHelper.floor(((double) (placer.rotationYaw * 4.0f / 360.0f)) + 2.5) & 3; + return getDefaultState().withProperty(type, DefaultPeripheral.values()[meta]).withProperty(facing, rot); + } + + @Override + public void getSubBlocks(CreativeTabs tab, NonNullList list) { + for(DefaultPeripheral dp : DefaultPeripheral.values()) + list.add(new ItemStack(getItem(), 1, dp.ordinal())); + } + + @Override + public IBlockState getStateFromMeta(int meta) { + return getDefaultState().withProperty(type, DefaultPeripheral.values()[meta & 3]).withProperty(facing, (meta >> 2) & 3); + } + + @Override + public int getMetaFromState(IBlockState state) { + return state.getValue(type).ordinal() | (state.getValue(facing) << 2); + } + + @Nullable + @Override + public TileEntity createNewTileEntity(World world, int meta) { + Class cls = DefaultPeripheral.values()[meta & 3].getTEClass(); + if(cls == null) + return null; + + try { + return cls.newInstance(); + } catch(Throwable t) { + Log.errorEx("Couldn't instantiate peripheral TileEntity:", t); + } + + return null; + } + + @Override + public EnumBlockRenderType getRenderType(IBlockState state) { + return EnumBlockRenderType.MODEL; + } + + @Override + public int damageDropped(IBlockState state) { + return state.getValue(type).ordinal(); + } + + @Override + public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) { + if(player.isSneaking()) + return false; + + if(player.getHeldItem(hand).getItem() instanceof ItemLinker) + return false; + + TileEntity te = world.getTileEntity(pos); + if(te == null || !(te instanceof TileEntityPeripheralBase)) + return false; + + return ((TileEntityPeripheralBase) te).onRightClick(player, hand, BlockSide.values()[facing.ordinal()]); + } + + @Override + public boolean isFullCube(IBlockState state) { + return state.getValue(type) != DefaultPeripheral.KEYBOARD; + } + + @Override + public boolean isFullBlock(IBlockState state) { + return state.getValue(type) != DefaultPeripheral.KEYBOARD; + } + + @Override + public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { + return state.getValue(type) != DefaultPeripheral.KEYBOARD; + } + + @Override + public boolean isOpaqueCube(IBlockState state) { + return state.getValue(type) != DefaultPeripheral.KEYBOARD; + } + + @Override + public boolean doesSideBlockRendering(IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing face) { + return state.getValue(type) != DefaultPeripheral.KEYBOARD; + } + + @Override + public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos) { + return state.getValue(type) == DefaultPeripheral.KEYBOARD ? BlockKeyboardRight.KEYBOARD_AABB : FULL_BLOCK_AABB; + } + + @Override + public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack) { + if(world.isRemote || state.getValue(type) != DefaultPeripheral.KEYBOARD) + return; + + //Keyboard special treatment + int f = state.getValue(facing); + Vec3i dir = EnumFacing.getHorizontal(f).rotateY().getDirectionVec(); + BlockPos left = pos.add(dir); + BlockPos right = pos.subtract(dir); + + if(!world.isAirBlock(pos.down()) && BlockKeyboardRight.checkNeighborhood(world, pos, null)) { + if(world.isAirBlock(right) && !world.isAirBlock(right.down()) && BlockKeyboardRight.checkNeighborhood(world, right, pos)) { + world.setBlockState(right, WebDisplays.INSTANCE.blockKbRight.getDefaultState().withProperty(BlockKeyboardRight.facing, f)); + return; + } else if(world.isAirBlock(left) && !world.isAirBlock(left.down()) && BlockKeyboardRight.checkNeighborhood(world, left, pos)) { + world.setBlockState(left, state); + world.setBlockState(pos, WebDisplays.INSTANCE.blockKbRight.getDefaultState().withProperty(BlockKeyboardRight.facing, f)); + return; + } + } + + //Not good; remove this shit... + world.setBlockToAir(pos); + if(!(placer instanceof EntityPlayer) || !((EntityPlayer) placer).isCreative()) + dropBlockAsItem(world, pos, state, 0); + } + + @Override + public EnumPushReaction getMobilityFlag(IBlockState state) { + return EnumPushReaction.IGNORE; + } + + private void removeRightPiece(World world, BlockPos pos) { + for(EnumFacing nf: EnumFacing.HORIZONTALS) { + BlockPos np = pos.add(nf.getDirectionVec()); + + if(world.getBlockState(np).getBlock() instanceof BlockKeyboardRight) { + world.setBlockToAir(np); + break; + } + } + } + + @Override + public void neighborChanged(IBlockState state, World world, BlockPos pos, Block neighborType, BlockPos neighbor) { + if(world.isRemote || state.getValue(type) != DefaultPeripheral.KEYBOARD) + return; + + if(neighbor.getX() == pos.getX() && neighbor.getY() == pos.getY() - 1 && neighbor.getZ() == pos.getZ() && world.isAirBlock(neighbor)) { + removeRightPiece(world, pos); + world.setBlockToAir(pos); + dropBlockAsItem(world, pos, state, 0); + } + } + + @Override + public void onBlockDestroyedByPlayer(World world, BlockPos pos, IBlockState state) { + if(!world.isRemote && state.getValue(type) == DefaultPeripheral.KEYBOARD) + removeRightPiece(world, pos); + } + + @Override + public void onBlockDestroyedByExplosion(World world, BlockPos pos, Explosion explosion) { + if(!world.isRemote && world.getBlockState(pos).getValue(type) == DefaultPeripheral.KEYBOARD) + removeRightPiece(world, pos); + } + + @Override + public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) { + if(!world.isRemote && world.getBlockState(pos).getValue(type) == DefaultPeripheral.KEYBOARD) { + double rpos = (entity.posY - ((double) pos.getY())) * 16.0; + + if(rpos >= 1.0 && rpos <= 2.0 && Math.random() < 0.25) { + TileEntity te = world.getTileEntity(pos); + + if(te != null && te instanceof TileEntityKeyboard) + ((TileEntityKeyboard) te).simulateCat(entity); + } + } + } + +} diff --git a/src/main/java/net/montoyo/wd/block/BlockScreen.java b/src/main/java/net/montoyo/wd/block/BlockScreen.java new file mode 100644 index 0000000..c762029 --- /dev/null +++ b/src/main/java/net/montoyo/wd/block/BlockScreen.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.block; + +import net.minecraft.block.material.EnumPushReaction; +import net.minecraft.block.material.Material; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.properties.PropertyBool; +import net.minecraft.block.properties.PropertyInteger; +import net.minecraft.block.state.BlockStateContainer; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumBlockRenderType; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.Explosion; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.property.ExtendedBlockState; +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.ScreenRights; +import net.montoyo.wd.data.SetURLData; +import net.montoyo.wd.entity.TileEntityScreen; +import net.montoyo.wd.utilities.*; + +import javax.annotation.Nullable; + +public class BlockScreen extends WDBlockContainer { + + public static final PropertyBool hasTE = PropertyBool.create("haste"); + private static final IProperty[] properties = new IProperty[] { hasTE }; + public static final IUnlistedProperty[] sideFlags = new IUnlistedProperty[6]; + static { + for(int i = 0; i < sideFlags.length; i++) + sideFlags[i] = Properties.toUnlisted(PropertyInteger.create("neighbor" + i, 0, 15)); + } + + public static final int BAR_BOT = 1; + public static final int BAR_RIGHT = 2; + public static final int BAR_TOP = 4; + public static final int BAR_LEFT = 8; + + public BlockScreen() { + super(Material.ROCK); + setHardness(1.5f); + setResistance(10.f); + setCreativeTab(WebDisplays.CREATIVE_TAB); + setName("screen"); + } + + @Override + public EnumBlockRenderType getRenderType(IBlockState state) { + return EnumBlockRenderType.MODEL; + } + + @Override + protected BlockStateContainer createBlockState() { + return new ExtendedBlockState(this, properties, sideFlags); + } + + public static boolean isScreenBlock(IBlockAccess world, Vector3i pos) { + return world.getBlockState(pos.toBlock()).getBlock() instanceof BlockScreen; + } + + @Override + public IBlockState getExtendedState(IBlockState state, IBlockAccess world, BlockPos bpos) { + IExtendedBlockState ret = (IExtendedBlockState) blockState.getBaseState(); + Vector3i pos = new Vector3i(bpos); + + for(BlockSide side : BlockSide.values()) { + int icon = 0; + if(!isScreenBlock(world, side.up.clone().add(pos))) icon |= BAR_TOP; + if(!isScreenBlock(world, side.down.clone().add(pos))) icon |= BAR_BOT; + if(!isScreenBlock(world, side.left.clone().add(pos))) icon |= BAR_LEFT; + if(!isScreenBlock(world, side.right.clone().add(pos))) icon |= BAR_RIGHT; + + ret = ret.withProperty(sideFlags[side.ordinal()], icon); + } + + return ret; + } + + @Override + public IBlockState getStateFromMeta(int meta) { + return getDefaultState().withProperty(hasTE, meta != 0); + } + + @Override + public int getMetaFromState(IBlockState state) { + return state.getValue(hasTE) ? 1 : 0; + } + + @Override + public boolean onBlockActivated(World world, BlockPos bpos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) { + if(!player.getHeldItem(hand).isEmpty()) + return false; + + if(world.isRemote) + return true; + + boolean sneaking = player.isSneaking(); + Vector3i pos = new Vector3i(bpos); + BlockSide side = BlockSide.values()[facing.ordinal()]; + + Multiblock.findOrigin(world, pos, side, null); + TileEntityScreen te = (TileEntityScreen) world.getTileEntity(pos.toBlock()); + + if(te != null && te.getScreen(side) != null) { + TileEntityScreen.Screen scr = te.getScreen(side); + + if(sneaking) { //Set URL + if((scr.rightsFor(player) & ScreenRights.CHANGE_URL) == 0) + Util.toast(player, "restrictions"); + else + (new SetURLData(pos, scr.side, scr.url)).sendTo((EntityPlayerMP) player); + + return true; + } else { + if((scr.rightsFor(player) & ScreenRights.CLICK) == 0) { + Util.toast(player, "restrictions"); + return true; + } + + if(side.right.x < 0) + hitX -= 1.f; + + if(side.right.z < 0 || side == BlockSide.TOP || side == BlockSide.BOTTOM) + hitZ -= 1.f; + + Vector3f rel = new Vector3f(bpos.getX(), bpos.getY(), bpos.getZ()); + rel.sub((float) pos.x, (float) pos.y, (float) pos.z); + rel.add(hitX, hitY, hitZ); + + float cx = rel.dot(side.right.toFloat()) - 2.f / 16.f; + float cy = rel.dot(side.up.toFloat()) - 2.f / 16.f; + float sw = ((float) scr.size.x) - 4.f / 16.f; + float sh = ((float) scr.size.y) - 4.f / 16.f; + + cx /= sw; + cy /= sh; + + if(cx >= 0.f && cx <= 1.0 && cy >= 0.f && cy <= 1.f) { + if(side != BlockSide.BOTTOM) + cy = 1.f - cy; + + cx *= (float) scr.resolution.x; + cy *= (float) scr.resolution.y; + te.click(side, new Vector2i((int) cx, (int) cy)); + } + + return true; + } + } else if(sneaking) { + Util.toast(player, "turnOn"); + return true; + } + + Vector2i size = Multiblock.measure(world, pos, side); + if(size.x < 2 || size.y < 2) { + Util.toast(player, "tooSmall"); + return true; + } + + Vector3i err = Multiblock.check(world, pos, size, side); + if(err != null) { + Util.toast(player, "invalid", err.toString()); + return true; + } + + boolean created = false; + Log.info("Structure at %s of size %dx%d", pos.toString(), size.x, size.y); + + if(te == null) { + BlockPos bp = pos.toBlock(); + world.setBlockState(bp, getDefaultState().withProperty(hasTE, true)); + te = (TileEntityScreen) world.getTileEntity(bp); + created = true; + } + + te.addScreen(side, size, null, !created).setOwner(player); + return true; + } + + @Nullable + @Override + public TileEntity createNewTileEntity(World world, int meta) { + return meta == 0 ? null : new TileEntityScreen(); + } + + @Override + public void onBlockDestroyedByPlayer(World world, BlockPos pos, IBlockState dontCare) { + if(!world.isRemote) { + Vector3i bp = new Vector3i(pos); + Multiblock.BlockOverride override = new Multiblock.BlockOverride(bp, Multiblock.OverrideAction.SIMULATE); + + for(BlockSide bs: BlockSide.values()) + destroySide(world, bp.clone(), bs, override); + } + } + + private void destroySide(World world, Vector3i pos, BlockSide side, Multiblock.BlockOverride override) { + Multiblock.findOrigin(world, pos, side, override); + BlockPos bp = pos.toBlock(); + TileEntity te = world.getTileEntity(bp); + + if(te != null && te instanceof TileEntityScreen) + world.setBlockState(bp, getDefaultState().withProperty(hasTE, false)); //Destroy tile entity + } + + @Override + public EnumPushReaction getMobilityFlag(IBlockState state) { + return EnumPushReaction.IGNORE; + } + + @Override + public void onBlockDestroyedByExplosion(World world, BlockPos pos, Explosion explosion) { + onBlockDestroyedByPlayer(world, pos, null); + } + + @Override + public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase whoDidThisShit, ItemStack stack) { + if(world.isRemote) + return; + + Multiblock.BlockOverride override = new Multiblock.BlockOverride(new Vector3i(pos), Multiblock.OverrideAction.IGNORE); + Vector3i[] neighbors = new Vector3i[6]; + + neighbors[0] = new Vector3i(pos.getX() + 1, pos.getY(), pos.getZ()); + neighbors[1] = new Vector3i(pos.getX() - 1, pos.getY(), pos.getZ()); + neighbors[2] = new Vector3i(pos.getX(), pos.getY() + 1, pos.getZ()); + neighbors[3] = new Vector3i(pos.getX(), pos.getY() - 1, pos.getZ()); + neighbors[4] = new Vector3i(pos.getX(), pos.getY(), pos.getZ() + 1); + neighbors[5] = new Vector3i(pos.getX(), pos.getY(), pos.getZ() - 1); + + for(Vector3i neighbor: neighbors) { + if(world.getBlockState(neighbor.toBlock()).getBlock() instanceof BlockScreen) { + for(BlockSide bs: BlockSide.values()) + destroySide(world, neighbor.clone(), bs, override); + } + } + } + +} diff --git a/src/main/java/net/montoyo/wd/block/WDBlock.java b/src/main/java/net/montoyo/wd/block/WDBlock.java new file mode 100644 index 0000000..4dab4a7 --- /dev/null +++ b/src/main/java/net/montoyo/wd/block/WDBlock.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.block; + +import net.minecraft.block.Block; +import net.minecraft.block.material.MapColor; +import net.minecraft.block.material.Material; +import net.minecraft.item.ItemBlock; + +public abstract class WDBlock extends Block { + + protected ItemBlock itemBlock; + + public WDBlock(Material mat, MapColor color) { + super(mat, color); + } + + public WDBlock(Material material) { + super(material); + } + + protected void setName(String name) { + setUnlocalizedName("webdisplays." + name); + setRegistryName(name); + } + + public void makeItemBlock() { + if(itemBlock != null) + throw new RuntimeException("WDBlock.makeItemBlock() called twice!"); + + itemBlock = new ItemBlock(this); + itemBlock.setUnlocalizedName(getUnlocalizedName()); + itemBlock.setRegistryName(getRegistryName()); + } + + public ItemBlock getItem() { + return itemBlock; + } + +} diff --git a/src/main/java/net/montoyo/wd/block/WDBlockContainer.java b/src/main/java/net/montoyo/wd/block/WDBlockContainer.java new file mode 100644 index 0000000..be2589e --- /dev/null +++ b/src/main/java/net/montoyo/wd/block/WDBlockContainer.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.block; + +import net.minecraft.block.BlockContainer; +import net.minecraft.block.material.MapColor; +import net.minecraft.block.material.Material; +import net.minecraft.item.ItemBlock; + +public abstract class WDBlockContainer extends BlockContainer { + + protected ItemBlock itemBlock; + + public WDBlockContainer(Material mat, MapColor color) { + super(mat, color); + } + + public WDBlockContainer(Material material) { + super(material); + } + + protected void setName(String name) { + setUnlocalizedName("webdisplays." + name); + setRegistryName(name); + } + + protected ItemBlock createItemBlock() { + return new ItemBlock(this); + } + + public void makeItemBlock() { + if(itemBlock != null) + throw new RuntimeException("WDBlockContainer.makeItemBlock() called twice!"); + + itemBlock = createItemBlock(); + itemBlock.setUnlocalizedName(getUnlocalizedName()); + itemBlock.setRegistryName(getRegistryName()); + } + + public ItemBlock getItem() { + return itemBlock; + } + +} diff --git a/src/main/java/net/montoyo/wd/client/ClientProxy.java b/src/main/java/net/montoyo/wd/client/ClientProxy.java new file mode 100644 index 0000000..5f85b56 --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/ClientProxy.java @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client; + +import com.mojang.authlib.GameProfile; +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +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.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumHandSide; +import net.minecraft.util.NonNullList; +import net.minecraft.world.World; +import net.minecraftforge.client.event.ModelBakeEvent; +import net.minecraftforge.client.event.ModelRegistryEvent; +import net.minecraftforge.client.event.RenderSpecificHandEvent; +import net.minecraftforge.client.event.TextureStitchEvent; +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.common.MinecraftForge; +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.wd.SharedProxy; +import net.montoyo.wd.WebDisplays; +import net.montoyo.wd.client.gui.GuiMinePad; +import net.montoyo.wd.client.gui.GuiScreenConfig; +import net.montoyo.wd.client.gui.GuiSetURL2; +import net.montoyo.wd.client.gui.WDScreen; +import net.montoyo.wd.client.gui.loading.GuiLoader; +import net.montoyo.wd.client.renderers.IModelBaker; +import net.montoyo.wd.client.renderers.MinePadRenderer; +import net.montoyo.wd.client.renderers.ScreenBaker; +import net.montoyo.wd.client.renderers.ScreenRenderer; +import net.montoyo.wd.data.GuiData; +import net.montoyo.wd.entity.TileEntityScreen; +import net.montoyo.wd.net.SMessagePadCtrl; +import net.montoyo.wd.utilities.*; + +import java.util.ArrayList; +import java.util.HashMap; + +public class ClientProxy extends SharedProxy implements IResourceManagerReloadListener, IDisplayHandler { + + public class PadData { + + public IBrowser view; + private boolean isInHotbar; + private int id; + private long lastURLSent; + + private PadData(String url, int id) { + view = mcef.createBrowser(url); + view.resize((int) WebDisplays.INSTANCE.padResX, (int) WebDisplays.INSTANCE.padResY); + isInHotbar = true; + this.id = id; + } + + } + + private Minecraft mc; + private ArrayList modelBakers = new ArrayList<>(); + private net.montoyo.mcef.api.API mcef; + private MinePadRenderer minePadRenderer; + + //Tracking + private ArrayList screenTracking = new ArrayList<>(); + private double unloadDistance2 = 32.0 * 32.0; + private double loadDistance2 = 30.0 * 30.0; + private int lastTracked = 0; + + //MinePads Management + private HashMap padMap = new HashMap<>(); + private ArrayList padList = new ArrayList<>(); + private int minePadTickCounter = 0; + + /**************************************** INHERITED METHODS ****************************************/ + + @Override + public void preInit() { + mc = Minecraft.getMinecraft(); + MinecraftForge.EVENT_BUS.register(this); + registerCustomBlockBaker(new ScreenBaker(), WebDisplays.INSTANCE.blockScreen); + } + + @Override + public void init() { + ClientRegistry.bindTileEntitySpecialRenderer(TileEntityScreen.class, new ScreenRenderer()); + mcef = MCEFApi.getAPI(); + minePadRenderer = new MinePadRenderer(); + } + + @Override + public void postInit() { + ((SimpleReloadableResourceManager) mc.getResourceManager()).registerReloadListener(this); + + if(mcef == null) + throw new RuntimeException("MCEF is missing"); + + mcef.registerDisplayHandler(this); + } + + @Override + public World getWorld(int dim) { + World ret = mc.world; + if(dim == CURRENT_DIMENSION) + return ret; + + if(ret.provider.getDimension() != dim) + throw new RuntimeException("Can't get non-current dimension " + dim + " from client."); + + return ret; + } + + @Override + public void enqueue(Runnable r) { + mc.addScheduledTask(r); + } + + @Override + public void displayGui(GuiData data) { + GuiScreen gui = data.createGui(mc.currentScreen, mc.world); + if(gui != null) + mc.displayGuiScreen(gui); + } + + @Override + public void trackScreen(TileEntityScreen tes, boolean track) { + int idx = -1; + for(int i = 0; i < screenTracking.size(); i++) { + if(screenTracking.get(i) == tes) { + idx = i; + break; + } + } + + if(track) { + if(idx < 0) + screenTracking.add(tes); + } else + screenTracking.remove(idx); + } + + @Override + public void onAutocompleteResult(NameUUIDPair[] pairs) { + if(mc.currentScreen != null && mc.currentScreen instanceof WDScreen) { + if(pairs.length == 0) + ((WDScreen) mc.currentScreen).onAutocompleteFailure(); + else + ((WDScreen) mc.currentScreen).onAutocompleteResult(pairs); + } + } + + @Override + public GameProfile[] getOnlineGameProfiles() { + return new GameProfile[] { mc.player.getGameProfile() }; + } + + @Override + public void screenUpdateResolutionInGui(Vector3i pos, BlockSide side, Vector2i res) { + if(mc.currentScreen != null && mc.currentScreen instanceof GuiScreenConfig) { + GuiScreenConfig gsc = (GuiScreenConfig) mc.currentScreen; + + if(gsc.isScreen(pos, side)) + gsc.updateResolution(res); + } + } + + @Override + public void displaySetPadURLGui(String padURL) { + mc.displayGuiScreen(new GuiSetURL2(padURL)); + } + + @Override + public void openMinePadGui(int padId) { + PadData pd = padMap.get(padId); + + if(pd != null && pd.view != null) + mc.displayGuiScreen(new GuiMinePad(pd)); + } + + /**************************************** RESOURCE MANAGER METHODS ****************************************/ + + @Override + public void onResourceManagerReload(IResourceManager rm) { + Log.info("Resource manager reload: clearing GUI cache..."); + GuiLoader.clearCache(); + } + + /**************************************** DISPLAY HANDLER METHODS ****************************************/ + + @Override + public void onAddressChange(IBrowser browser, String url) { + if(browser != null) { + long t = System.currentTimeMillis(); + + for(PadData pd : padList) { + if(pd.view == browser && t - pd.lastURLSent >= 1000) { + pd.lastURLSent = t; //Avoid spamming the server with porn URLs + WebDisplays.NET_HANDLER.sendToServer(new SMessagePadCtrl(pd.id, url)); + break; + } + } + } + } + + @Override + public void onTitleChange(IBrowser browser, String title) { + } + + @Override + public void onTooltip(IBrowser browser, String text) { + } + + @Override + public void onStatusMessage(IBrowser browser, String value) { + } + + /**************************************** EVENT METHODS ****************************************/ + + @SubscribeEvent + public void onStitchTextures(TextureStitchEvent.Pre ev) { + TextureMap texMap = ev.getMap(); + + if(texMap == mc.getTextureMapBlocks()) { + for(ResourceModelPair pair : modelBakers) + pair.getModel().loadTextures(texMap); + } + } + + @SubscribeEvent + public void onBakeModel(ModelBakeEvent ev) { + for(ResourceModelPair pair : modelBakers) + ev.getModelRegistry().putObject(pair.getResourceLocation(), pair.getModel()); + } + + @SubscribeEvent + public void onRegisterModels(ModelRegistryEvent ev) { + final WebDisplays wd = WebDisplays.INSTANCE; + + registerItemModel(wd.blockScreen.getItem(), 0, "inventory"); + ModelLoader.setCustomModelResourceLocation(wd.blockPeripheral.getItem(), 0, new ModelResourceLocation("webdisplays:kb_inv", "normal")); + registerItemModel(wd.blockPeripheral.getItem(), 1, "facing=0,type=remotectrl"); + registerItemModel(wd.blockPeripheral.getItem(), 2, "facing=0,type=ccinterface"); //TODO: This doesn't work... + registerItemModel(wd.blockPeripheral.getItem(), 3, "facing=0,type=cointerface"); + registerItemModel(wd.itemScreenCfg, 0, "normal"); + registerItemModel(wd.itemOwnerThief, 0, "normal"); + registerItemModel(wd.itemLinker, 0, "normal"); + registerItemModel(wd.itemStoneKey, 0, "normal"); + registerItemModel(wd.itemMinePad, 0, "normal"); + } + + @SubscribeEvent + public void onTick(TickEvent.ClientTickEvent ev) { + if(ev.phase == TickEvent.Phase.END) { + //Unload/load screens depending on client player distance + if(mc.player != null && !screenTracking.isEmpty()) { + int id = lastTracked % screenTracking.size(); + lastTracked++; + + TileEntityScreen tes = screenTracking.get(id); + double dist2 = mc.player.getDistanceSq(tes.getPos()); + + if(tes.isLoaded()) { + if(dist2 > unloadDistance2) + tes.unload(); + else + tes.updateTrackDistance(dist2); + } else if(dist2 <= loadDistance2) + tes.load(); + } + + //Load/unload minePads depending on which item is in the player's hand + if(++minePadTickCounter >= 10) { + minePadTickCounter = 0; + EntityPlayer ep = mc.player; + + for(PadData pd: padList) + pd.isInHotbar = false; + + if(ep != null) { + updateInventory(ep.inventory.mainInventory, ep.getHeldItem(EnumHand.MAIN_HAND), 9); + updateInventory(ep.inventory.offHandInventory, ep.getHeldItem(EnumHand.OFF_HAND), 1); //Is this okay? + } + + //TODO: Check for GuiContainer.draggedStack + + for(int i = padList.size() - 1; i >= 0; i--) { + PadData pd = padList.get(i); + + if(!pd.isInHotbar) { + pd.view.close(); + pd.view = null; //This is for GuiMinePad, in case the player dies with the GUI open + padList.remove(i); + padMap.remove(pd.id); + } + } + } + } + } + + @SubscribeEvent + public void onRenderPlayerHand(RenderSpecificHandEvent ev) { + if(ev.getItemStack().getItem() == WebDisplays.INSTANCE.itemMinePad) { + EnumHandSide handSide = mc.player.getPrimaryHand(); + if(ev.getHand() == EnumHand.OFF_HAND) + handSide = handSide.opposite(); + + minePadRenderer.render(ev.getItemStack(), (handSide == EnumHandSide.RIGHT) ? 1.0f : -1.0f, ev.getSwingProgress(), ev.getEquipProgress()); + ev.setCanceled(true); + } + } + + /**************************************** OTHER METHODS ****************************************/ + + private void updateInventory(NonNullList inv, ItemStack heldStack, int cnt) { + for(int i = 0; i < cnt; i++) { + ItemStack item = inv.get(i); + + if(item.getItem() == WebDisplays.INSTANCE.itemMinePad) { + NBTTagCompound tag = item.getTagCompound(); + + if(tag != null && tag.hasKey("PadID")) + updatePad(tag.getInteger("PadID"), tag, item == heldStack); + } + } + } + + private void registerCustomBlockBaker(IModelBaker baker, Block block0) { + ModelResourceLocation normalLoc = new ModelResourceLocation(block0.getRegistryName(), "normal"); + ResourceModelPair pair = new ResourceModelPair(normalLoc, baker); + modelBakers.add(pair); + ModelLoader.setCustomStateMapper(block0, new StaticStateMapper(normalLoc)); + } + + private void registerItemModel(Item item, int meta, String variant) { + ModelLoader.setCustomModelResourceLocation(item, meta, new ModelResourceLocation(item.getRegistryName(), variant)); + } + + private void updatePad(int id, NBTTagCompound tag, boolean isSelected) { + PadData pd = padMap.get(id); + + if(pd != null) + pd.isInHotbar = true; + else if(isSelected && tag.hasKey("PadURL")) { + pd = new PadData(tag.getString("PadURL"), id); + padMap.put(id, pd); + padList.add(pd); + } + } + + public MinePadRenderer getMinePadRenderer() { + return minePadRenderer; + } + + public PadData getPadByID(int id) { + return padMap.get(id); + } + + public net.montoyo.mcef.api.API getMCEF() { + return mcef; + } + +} diff --git a/src/main/java/net/montoyo/wd/client/ResourceModelPair.java b/src/main/java/net/montoyo/wd/client/ResourceModelPair.java new file mode 100644 index 0000000..d70d55e --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/ResourceModelPair.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client; + +import net.minecraft.client.renderer.block.model.ModelResourceLocation; +import net.montoyo.wd.client.renderers.IModelBaker; + +public class ResourceModelPair { + + private ModelResourceLocation resLoc; + private IModelBaker model; + + public ResourceModelPair(ModelResourceLocation rl, IModelBaker m) { + resLoc = rl; + model = m; + } + + public ModelResourceLocation getResourceLocation() { + return resLoc; + } + + public IModelBaker getModel() { + return model; + } + +} diff --git a/src/main/java/net/montoyo/wd/client/StaticStateMapper.java b/src/main/java/net/montoyo/wd/client/StaticStateMapper.java new file mode 100644 index 0000000..1a06028 --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/StaticStateMapper.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.renderer.block.model.ModelResourceLocation; +import net.minecraft.client.renderer.block.statemap.StateMapperBase; + +public class StaticStateMapper extends StateMapperBase { + + private ModelResourceLocation resLoc; + + public StaticStateMapper(ModelResourceLocation rl) { + resLoc = rl; + } + + @Override + protected ModelResourceLocation getModelResourceLocation(IBlockState state) { + return resLoc; + } + +} diff --git a/src/main/java/net/montoyo/wd/client/gui/GuiKeyboard.java b/src/main/java/net/montoyo/wd/client/gui/GuiKeyboard.java new file mode 100644 index 0000000..8206b95 --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/gui/GuiKeyboard.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client.gui; + +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import net.montoyo.wd.WebDisplays; +import net.montoyo.wd.client.gui.controls.Button; +import net.montoyo.wd.client.gui.controls.Control; +import net.montoyo.wd.client.gui.controls.Label; +import net.montoyo.wd.client.gui.loading.FillControl; +import net.montoyo.wd.entity.TileEntityScreen; +import net.montoyo.wd.net.SMessageScreenCtrl; +import net.montoyo.wd.utilities.BlockSide; +import net.montoyo.wd.utilities.Log; +import net.montoyo.wd.utilities.Util; +import org.lwjgl.input.Keyboard; + +import java.io.*; +import java.util.Map; + +@SideOnly(Side.CLIENT) +public class GuiKeyboard extends WDScreen { + + private static final String WARNING_FNAME = "wd_keyboard_warning.txt"; + + private TileEntityScreen tes; + private BlockSide side; + private String eventStack = ""; + private boolean lastIsType = false; + private BlockPos kbPos; + private boolean showWarning = true; + + @FillControl + private Label lblInfo; + + @FillControl + private Button btnOk; + + public GuiKeyboard() { + } + + public GuiKeyboard(TileEntityScreen tes, BlockSide side, BlockPos kbPos) { + this.tes = tes; + this.side = side; + this.kbPos = kbPos; + } + + @Override + protected void addLoadCustomVariables(Map vars) { + vars.put("showWarning", showWarning ? 1.0 : 0.0); + } + + @Override + public void initGui() { + super.initGui(); + + if(mc.isIntegratedServerRunning() && mc.getIntegratedServer() != null && !mc.getIntegratedServer().getPublic()) + showWarning = false; //NO NEED + else + showWarning = !hasUserReadWarning(); + + loadFrom(new ResourceLocation("webdisplays", "gui/keyboard.json")); + + if(showWarning) { + int maxLabelW = 0; + int totalH = 0; + + for(Control ctrl : controls) { + if(ctrl != lblInfo && ctrl instanceof Label) { + if(ctrl.getWidth() > maxLabelW) + maxLabelW = ctrl.getWidth(); + + totalH += ctrl.getHeight(); + ctrl.setPos((width - ctrl.getWidth()) / 2, 0); + } + } + + btnOk.setWidth(maxLabelW); + btnOk.setPos((width - maxLabelW) / 2, 0); + totalH += btnOk.getHeight(); + + int y = (height - totalH) / 2; + for(Control ctrl : controls) { + if(ctrl != lblInfo) { + ctrl.setPos(ctrl.getX(), y); + y += ctrl.getHeight(); + } + } + } else { + mc.inGameHasFocus = true; + mc.mouseHelper.grabMouseCursor(); + } + + defaultBackground = showWarning; + syncTicks = 5; + } + + @Override + public void handleInput() { + if(showWarning) { + try { + super.handleInput(); + } catch(IOException ex) { + Log.warningEx("Caught exception while handling screen input", ex); + } + + return; + } + + if(Keyboard.isCreated()) { + while(Keyboard.next()) { + if(Keyboard.getEventKey() == Keyboard.KEY_ESCAPE) + mc.displayGuiScreen(null); + else { + char chr = Keyboard.getEventCharacter(); + + if(chr == '\n' || chr == '\r' || chr == '\b') { + if(lastIsType) + lastIsType = false; + + if(!eventStack.isEmpty()) + eventStack += (char) 1; + + if(Keyboard.getEventKeyState()) + eventStack += 'p'; + else + eventStack += 'r'; + + eventStack += chr; + } else if(chr != 0) { + if(!lastIsType) { + if(!eventStack.isEmpty()) + eventStack += (char) 1; + + eventStack += 't'; + lastIsType = true; + } + + eventStack += chr; + } + } + } + + if(!eventStack.isEmpty() && !syncRequested()) + requestSync(); + } + } + + @Override + public boolean doesGuiPauseGame() { + return false; + } + + @Override + protected void sync() { + if(!eventStack.isEmpty()) { + WebDisplays.NET_HANDLER.sendToServer(SMessageScreenCtrl.type(tes, side, eventStack, kbPos)); + eventStack = ""; + lastIsType = false; + } + } + + @GuiSubscribe + public void onClick(Button.ClickEvent ev) { + if(showWarning && ev.getSource() == btnOk) { + writeUserAcknowledge(); + + for(Control ctrl: controls) { + if(ctrl instanceof Label) { + Label lbl = (Label) ctrl; + lbl.setVisible(!lbl.isVisible()); + } + } + + btnOk.setDisabled(true); + btnOk.setVisible(false); + showWarning = false; + defaultBackground = false; + mc.inGameHasFocus = true; + mc.mouseHelper.grabMouseCursor(); + } + } + + private boolean hasUserReadWarning() { + try { + File f = new File(mc.mcDataDir, WARNING_FNAME); + + if(f.exists()) { + BufferedReader br = new BufferedReader(new FileReader(f)); + String str = br.readLine(); + Util.silentClose(br); + + return str != null && str.trim().equalsIgnoreCase("read"); + } + } catch(Throwable t) { + Log.warningEx("Can't know if user has already read the warning", t); + } + + return false; + } + + private void writeUserAcknowledge() { + try { + File f = new File(mc.mcDataDir, WARNING_FNAME); + + BufferedWriter bw = new BufferedWriter(new FileWriter(f)); + bw.write("read\n"); + Util.silentClose(bw); + } catch(Throwable t) { + Log.warningEx("Can't write that the user read the warning", t); + } + } + +} diff --git a/src/main/java/net/montoyo/wd/client/gui/GuiMinePad.java b/src/main/java/net/montoyo/wd/client/gui/GuiMinePad.java new file mode 100644 index 0000000..8642705 --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/gui/GuiMinePad.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client.gui; + +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import net.montoyo.wd.WebDisplays; +import net.montoyo.wd.client.ClientProxy; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; + +import static org.lwjgl.opengl.GL11.*; + +@SideOnly(Side.CLIENT) +public class GuiMinePad extends WDScreen { + + private ClientProxy.PadData pad; + private double vx; + private double vy; + private double vw; + private double vh; + + public GuiMinePad() { + } + + public GuiMinePad(ClientProxy.PadData pad) { + this.pad = pad; + } + + @Override + public void initGui() { + super.initGui(); + + vw = ((double) width) - 32.0f; + vh = vw / WebDisplays.PAD_RATIO; + vx = 16.0f; + vy = (((double) height) - vh) / 2.0f; + } + + private static void addRect(BufferBuilder bb, double x, double y, double w, double h) { + bb.pos(x, y, 0.0).endVertex(); + bb.pos(x + w, y, 0.0).endVertex(); + bb.pos(x + w, y + h, 0.0).endVertex(); + bb.pos(x, y + h, 0.0).endVertex(); + } + + @Override + public void drawScreen(int mouseX, int mouseY, float ptt) { + drawDefaultBackground(); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_CULL_FACE); + glColor4f(0.73f, 0.73f, 0.73f, 1.0f); + + Tessellator t = Tessellator.getInstance(); + BufferBuilder bb = t.getBuffer(); + bb.begin(GL_QUADS, DefaultVertexFormats.POSITION); + addRect(bb, vx, vy - 16, vw, 16); + addRect(bb, vx, vy + vh, vw, 16); + addRect(bb, vx - 16, vy, 16, vh); + addRect(bb, vx + vw, vy, 16, vh); + t.draw(); + + glEnable(GL_TEXTURE_2D); + + if(pad.view != null) { + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + pad.view.draw(vx, vy + vh, vx + vw, vy); + } + + glEnable(GL_CULL_FACE); + } + + @Override + public void handleInput() { + while(Keyboard.next()) { + char key = Keyboard.getEventCharacter(); + boolean pressed = Keyboard.getEventKeyState(); + + if(Keyboard.getEventKey() == Keyboard.KEY_ESCAPE) { + mc.displayGuiScreen(null); + return; + } + + if(pad.view != null) { + if(key != '.' && key != ';' && key != ',') { + if(pressed) + pad.view.injectKeyPressed(key, 0); + else + pad.view.injectKeyReleased(key, 0); + } + + if(key != Keyboard.CHAR_NONE) + pad.view.injectKeyTyped(key, 0); + } + } + + int vx = screen2DisplayX((int) this.vx); + int vy = screen2DisplayY((int) this.vy); + int vh = screen2DisplayX((int) this.vh); + int vw = screen2DisplayY((int) this.vw); + + while(Mouse.next()) { + int btn = Mouse.getEventButton(); + boolean pressed = Mouse.getEventButtonState(); + int sx = Mouse.getEventX(); + int sy = Mouse.getEventY(); + + if(pad.view != null && sx >= vx && sx <= vx + vw && sy >= vy && sy <= vy + vh) { + sx -= vx; + sy -= vy; + sy = vh - sy; + + //Scale again according to the webview + sx = (int) (((double) sx) / ((double) vw) * WebDisplays.INSTANCE.padResX); + sy = (int) (((double) sy) / ((double) vh) * WebDisplays.INSTANCE.padResY); + + if(btn == -1) + pad.view.injectMouseMove(sx, sy, 0, false); + else + pad.view.injectMouseButton(sx, sy, 0, btn + 1, pressed, 1); + } + } + } + + @Override + public void updateScreen() { + if(pad.view == null) + mc.displayGuiScreen(null); //In case the user dies with the pad in the hand + } + + @Override + public boolean doesGuiPauseGame() { + //TODO: Is this necessary?? + return false; + } + +} diff --git a/src/main/java/net/montoyo/wd/client/gui/GuiScreenConfig.java b/src/main/java/net/montoyo/wd/client/gui/GuiScreenConfig.java new file mode 100644 index 0000000..bce5f7f --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/gui/GuiScreenConfig.java @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client.gui; + +import net.minecraft.util.ResourceLocation; +import net.montoyo.wd.WebDisplays; +import net.montoyo.wd.client.gui.controls.*; +import net.montoyo.wd.client.gui.loading.FillControl; +import net.montoyo.wd.core.ScreenRights; +import net.montoyo.wd.entity.TileEntityScreen; +import net.montoyo.wd.net.SMessageScreenCtrl; +import net.montoyo.wd.utilities.*; + +import java.util.ArrayList; +import java.util.Arrays; + +public class GuiScreenConfig extends WDScreen { + + //Screen data + private TileEntityScreen tes; + private BlockSide side; + private NameUUIDPair owner; + private NameUUIDPair[] friends; + private int friendRights; + private int otherRights; + + //Autocomplete handling + private boolean waitingAC; + private int acFailTicks = -1; + + private ArrayList acResults = new ArrayList(); + private boolean adding; + + //Controls + @FillControl + private Label lblOwner; + + @FillControl + private List lstFriends; + + @FillControl + private Button btnAdd; + + @FillControl + private TextField tfFriend; + + @FillControl + private TextField tfResX; + + @FillControl + private TextField tfResY; + + @FillControl + private ControlGroup grpFriends; + + @FillControl + private ControlGroup grpOthers; + + @FillControl + private CheckBox boxFSetUrl; + + @FillControl + private CheckBox boxFClick; + + @FillControl + private CheckBox boxFFriends; + + @FillControl + private CheckBox boxFOthers; + + @FillControl + private CheckBox boxFUpgrades; + + @FillControl + private CheckBox boxFResolution; + + @FillControl + private CheckBox boxOSetUrl; + + @FillControl + private CheckBox boxOClick; + + @FillControl + private CheckBox boxOUpgrades; + + @FillControl + private CheckBox boxOResolution; + + @FillControl + private Button btnSetRes; + + private CheckBox[] friendBoxes; + private CheckBox[] otherBoxes; + + public GuiScreenConfig(TileEntityScreen tes, BlockSide side, NameUUIDPair owner, NameUUIDPair[] friends, int fr, int or) { + this.tes = tes; + this.side = side; + this.owner = owner; + this.friends = friends; + friendRights = fr; + otherRights = or; + } + + @Override + public void initGui() { + super.initGui(); + loadFrom(new ResourceLocation("webdisplays", "gui/screencfg.json")); + + friendBoxes = new CheckBox[] { boxFResolution, boxFUpgrades, boxFOthers, boxFFriends, boxFClick, boxFSetUrl }; + boxFResolution.setUserdata(ScreenRights.CHANGE_RESOLUTION); + boxFUpgrades.setUserdata(ScreenRights.MANAGE_UPGRADES); + boxFOthers.setUserdata(ScreenRights.MANAGE_OTHER_RIGHTS); + boxFFriends.setUserdata(ScreenRights.MANAGE_FRIEND_LIST); + boxFClick.setUserdata(ScreenRights.CLICK); + boxFSetUrl.setUserdata(ScreenRights.CHANGE_URL); + + otherBoxes = new CheckBox[] { boxOResolution, boxOUpgrades, boxOClick, boxOSetUrl }; + boxOResolution.setUserdata(ScreenRights.CHANGE_RESOLUTION); + boxOUpgrades.setUserdata(ScreenRights.MANAGE_UPGRADES); + boxOClick.setUserdata(ScreenRights.CLICK); + boxOSetUrl.setUserdata(ScreenRights.CHANGE_URL); + + TileEntityScreen.Screen scr = tes.getScreen(side); + if(scr != null) { + tfResX.setText("" + scr.resolution.x); + tfResY.setText("" + scr.resolution.y); + } + + lblOwner.setLabel(lblOwner.getLabel() + owner.name); + for(NameUUIDPair f : friends) + lstFriends.addElementRaw(f.name, f); + + lstFriends.updateContent(); + updateRights(friendRights, friendRights, friendBoxes, true); + updateRights(otherRights, otherRights, otherBoxes, true); + updateMyRights(); + } + + private void addFriend(String name) { + if(!name.isEmpty()) { + requestAutocomplete(name, true); + tfFriend.setDisabled(true); + adding = true; + waitingAC = true; + } + } + + private void clickSetRes() { + TileEntityScreen.Screen scr = tes.getScreen(side); + if(scr == null) + return; //WHATDAFUQ? + + try { + int x = Integer.parseInt(tfResX.getText()); + int y = Integer.parseInt(tfResY.getText()); + if(x < 1 || y < 1) + throw new NumberFormatException(); //I'm lazy + + if(x != scr.resolution.x || y != scr.resolution.y) + WebDisplays.NET_HANDLER.sendToServer(new SMessageScreenCtrl(tes, side, new Vector2i(x, y))); + } catch(NumberFormatException ex) { + //Roll back + tfResX.setText("" + scr.resolution.x); + tfResY.setText("" + scr.resolution.y); + } + + btnSetRes.setDisabled(true); + } + + @GuiSubscribe + public void onClick(Button.ClickEvent ev) { + if(ev.getSource() == btnAdd && !waitingAC) + addFriend(tfFriend.getText().trim()); + else if(ev.getSource() == btnSetRes) + clickSetRes(); + } + + @GuiSubscribe + public void onEnterPressed(TextField.EnterPressedEvent ev) { + if(ev.getSource() == tfFriend && !waitingAC) + addFriend(ev.getText().trim()); + else if((ev.getSource() == tfResX || ev.getSource() == tfResY) && !btnSetRes.isDisabled()) + clickSetRes(); + } + + @GuiSubscribe + public void onAutocomplete(TextField.TabPressedEvent ev) { + if(ev.getSource() == tfFriend && !waitingAC && !ev.getBeginning().isEmpty()) { + if(acResults.isEmpty()) { + waitingAC = true; + requestAutocomplete(ev.getBeginning(), false); + } else { + NameUUIDPair pair = acResults.remove(0); + tfFriend.setText(pair.name); + } + } else if(ev.getSource() == tfResX) { + tfResX.setFocused(false); + tfResY.focus(); + tfResY.getMcField().setCursorPositionZero(); + tfResY.getMcField().setSelectionPos(tfResY.getText().length()); + } + } + + @GuiSubscribe + public void onTextChanged(TextField.TextChangedEvent ev) { + if(ev.getSource() == tfResX || ev.getSource() == tfResY) { + for(int i = 0; i < ev.getNewContent().length(); i++) { + if(!Character.isDigit(ev.getNewContent().charAt(i))) { + ev.getSource().setText(ev.getOldContent()); + return; + } + } + + btnSetRes.setDisabled(false); + } + } + + @GuiSubscribe + public void onRemovePlayer(List.EntryClick ev) { + if(ev.getSource() == lstFriends) + WebDisplays.NET_HANDLER.sendToServer(new SMessageScreenCtrl(tes, side, (NameUUIDPair) ev.getUserdata(), true)); + } + + @GuiSubscribe + public void onCheckboxChanged(CheckBox.CheckedEvent ev) { + if(isFriendCheckbox(ev.getSource())) { + int flag = (Integer) ev.getSource().getUserdata(); + if(ev.isChecked()) + friendRights |= flag; + else + friendRights &= ~flag; + + requestSync(); + } else if(isOtherCheckbox(ev.getSource())) { + int flag = (Integer) ev.getSource().getUserdata(); + if(ev.isChecked()) + otherRights |= flag; + else + otherRights &= ~flag; + + requestSync(); + } + } + + public boolean isFriendCheckbox(CheckBox cb) { + for(CheckBox box : friendBoxes) { + if(box == cb) + return true; + } + + return false; + } + + public boolean isOtherCheckbox(CheckBox cb) { + for(CheckBox box : otherBoxes) { + if(box == cb) + return true; + } + + return false; + } + + public boolean hasFriend(NameUUIDPair f) { + for(NameUUIDPair pair : friends) { + if(pair.equals(f)) + return true; + } + + return false; + } + + @Override + public void onAutocompleteResult(NameUUIDPair pairs[]) { + waitingAC = false; + + if(adding) { + if(!hasFriend(pairs[0])) + WebDisplays.NET_HANDLER.sendToServer(new SMessageScreenCtrl(tes, side, pairs[0], false)); + + tfFriend.setDisabled(false); + tfFriend.clear(); + tfFriend.focus(); + adding = false; + } else { + acResults.clear(); + acResults.addAll(Arrays.asList(pairs)); + + NameUUIDPair pair = acResults.remove(0); + tfFriend.setText(pair.name); + } + } + + @Override + public void onAutocompleteFailure() { + waitingAC = false; + acResults.clear(); + acFailTicks = 0; + tfFriend.setTextColor(Control.COLOR_RED); + + if(adding) { + tfFriend.setDisabled(false); + adding = false; + } + } + + @Override + public void updateScreen() { + super.updateScreen(); + + if(acFailTicks >= 0) { + if(++acFailTicks >= 10) { + acFailTicks = -1; + tfFriend.setTextColor(TextField.DEFAULT_TEXT_COLOR); + } + } + } + + public void updateFriends(NameUUIDPair[] friends) { + boolean diff = false; + if(friends.length != this.friends.length) + diff = true; + else { + for(NameUUIDPair pair : friends) { + if(!hasFriend(pair)) { + diff = true; + break; + } + } + } + + if(diff) { + this.friends = friends; + lstFriends.clearRaw(); + for(NameUUIDPair pair : friends) + lstFriends.addElementRaw(pair.name, pair); + + lstFriends.updateContent(); + } + } + + private int updateRights(int current, int newVal, CheckBox[] boxes, boolean force) { + if(force || current != newVal) { + for(CheckBox box : boxes) { + int flag = (Integer) box.getUserdata(); + box.setChecked((newVal & flag) != 0); + } + + if(!force) { + Log.info("Screen check boxes were updated"); + abortSync(); //Value changed by another user, abort modifications by local user + } + } + + return newVal; + } + + public void updateFriendRights(int rights) { + friendRights = updateRights(friendRights, rights, friendBoxes, false); + } + + public void updateOtherRights(int rights) { + otherRights = updateRights(otherRights, rights, otherBoxes, false); + } + + public boolean isScreen(Vector3i pos, BlockSide side) { + return pos.x == tes.getPos().getX() && pos.y == tes.getPos().getY() && pos.z == tes.getPos().getZ() && side == this.side; + } + + @Override + protected void sync() { + WebDisplays.NET_HANDLER.sendToServer(new SMessageScreenCtrl(tes, side, friendRights, otherRights)); + Log.info("Sent sync packet"); + } + + public void updateMyRights() { + NameUUIDPair me = new NameUUIDPair(mc.player.getGameProfile()); + int myRights = 0; + boolean clientIsOwner = false; + + if(me.equals(owner)) { + myRights = ScreenRights.ALL; + clientIsOwner = true; + } else if(hasFriend(me)) + myRights = friendRights; + else + myRights = otherRights; + + //Disable components according to client rights + grpFriends.setDisabled(!clientIsOwner); + + boolean flag = (myRights & ScreenRights.MANAGE_FRIEND_LIST) == 0; + lstFriends.setDisabled(flag); + tfFriend.setDisabled(flag); + btnAdd.setDisabled(flag); + + flag = (myRights & ScreenRights.MANAGE_OTHER_RIGHTS) == 0; + grpOthers.setDisabled(flag); + + flag = (myRights & ScreenRights.CHANGE_RESOLUTION) == 0; + tfResX.setDisabled(flag); + tfResY.setDisabled(flag); + + if(flag) + btnSetRes.setDisabled(true); + } + + public void updateResolution(Vector2i res) { + tfResX.setText("" + res.x); + tfResY.setText("" + res.y); + btnSetRes.setDisabled(true); + } + +} diff --git a/src/main/java/net/montoyo/wd/client/gui/GuiSetURL2.java b/src/main/java/net/montoyo/wd/client/gui/GuiSetURL2.java new file mode 100644 index 0000000..ae0ed6f --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/gui/GuiSetURL2.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client.gui; + +import net.minecraft.util.ResourceLocation; +import net.montoyo.wd.WebDisplays; +import net.montoyo.wd.client.gui.controls.Button; +import net.montoyo.wd.client.gui.controls.TextField; +import net.montoyo.wd.client.gui.loading.FillControl; +import net.montoyo.wd.entity.TileEntityScreen; +import net.montoyo.wd.net.SMessagePadCtrl; +import net.montoyo.wd.net.SMessageScreenCtrl; +import net.montoyo.wd.utilities.BlockSide; +import net.montoyo.wd.utilities.Log; + +import java.util.Map; + +public class GuiSetURL2 extends WDScreen { + + //Screen data + private TileEntityScreen tileEntity; + private BlockSide screenSide; + + //Pad data + private boolean isPad; + + //Common + private String screenURL; + + @FillControl + private TextField tfURL; + + @FillControl + private Button btnShutDown; + + @FillControl + private Button btnCancel; + + @FillControl + private Button btnOk; + + @FillControl + private Button btnYT; + + public GuiSetURL2(TileEntityScreen tes, BlockSide side, String url) { + tileEntity = tes; + screenSide = side; + screenURL = url; + isPad = false; + } + + public GuiSetURL2(String url) { + screenURL = url; + isPad = true; + } + + @Override + public void initGui() { + super.initGui(); + loadFrom(new ResourceLocation("webdisplays", "gui/seturl.json")); + tfURL.setText(screenURL); + } + + @Override + protected void addLoadCustomVariables(Map vars) { + vars.put("isPad", isPad ? 1.0 : 0.0); + } + + @GuiSubscribe + public void onButtonClicked(Button.ClickEvent ev) { + if(ev.getSource() == btnCancel) + mc.displayGuiScreen(null); + else if(ev.getSource() == btnOk) + validate(tfURL.getText()); + else if(ev.getSource() == btnShutDown) { + if(isPad) + WebDisplays.NET_HANDLER.sendToServer(new SMessagePadCtrl("")); + + mc.displayGuiScreen(null); + } else + Log.info("GuiSetURL2: TODO"); //TODO + } + + @GuiSubscribe + public void onEnterPressed(TextField.EnterPressedEvent ev) { + validate(ev.getText()); + } + + private void validate(String url) { + if(!url.isEmpty()) { + if(isPad) + WebDisplays.NET_HANDLER.sendToServer(new SMessagePadCtrl(url)); + else + WebDisplays.NET_HANDLER.sendToServer(new SMessageScreenCtrl(tileEntity, screenSide, url)); + } + + mc.displayGuiScreen(null); + } + +} diff --git a/src/main/java/net/montoyo/wd/client/gui/GuiSubscribe.java b/src/main/java/net/montoyo/wd/client/gui/GuiSubscribe.java new file mode 100644 index 0000000..5814b77 --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/gui/GuiSubscribe.java @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client.gui; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface GuiSubscribe { +} diff --git a/src/main/java/net/montoyo/wd/client/gui/WDScreen.java b/src/main/java/net/montoyo/wd/client/gui/WDScreen.java new file mode 100644 index 0000000..4164e96 --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/gui/WDScreen.java @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client.gui; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.util.ResourceLocation; +import net.montoyo.wd.WebDisplays; +import net.montoyo.wd.client.gui.controls.Container; +import net.montoyo.wd.client.gui.controls.Control; +import net.montoyo.wd.client.gui.controls.Event; +import net.montoyo.wd.client.gui.loading.FillControl; +import net.montoyo.wd.client.gui.loading.GuiLoader; +import net.montoyo.wd.client.gui.loading.JsonOWrapper; +import net.montoyo.wd.net.SMessageACQuery; +import net.montoyo.wd.utilities.Log; +import net.montoyo.wd.utilities.NameUUIDPair; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +public abstract class WDScreen extends GuiScreen { + + public static WDScreen CURRENT_SCREEN = null; + + protected ArrayList controls = new ArrayList(); + private HashMap, Method> eventMap = new HashMap, Method>(); + protected boolean quitOnEscape = true; + protected boolean defaultBackground = true; + protected int syncTicks = 40; + private int syncTicksLeft = -1; + + public WDScreen() { + Method[] methods = getClass().getMethods(); + + for(Method m : methods) { + if(m.getAnnotation(GuiSubscribe.class) != null) { + if(!Modifier.isPublic(m.getModifiers())) + throw new RuntimeException("Found non public @GuiSubscribe"); + + Class params[] = m.getParameterTypes(); + if(params.length != 1 || !Event.class.isAssignableFrom(params[0])) + throw new RuntimeException("Invalid parameters for @GuiSubscribe"); + + eventMap.put((Class) params[0], m); + } + } + } + + protected T addControl(T ctrl) { + controls.add(ctrl); + return ctrl; + } + + public int screen2DisplayX(int x) { + double ret = ((double) x) / ((double) width) * ((double) mc.displayWidth); + return (int) ret; + } + + public int screen2DisplayY(int y) { + double ret = ((double) y) / ((double) height) * ((double) mc.displayHeight); + return (int) ret; + } + + public int display2ScreenX(int x) { + double ret = ((double) x) / ((double) mc.displayWidth) * ((double) width); + return (int) ret; + } + + public int display2ScreenY(int y) { + double ret = ((double) y) / ((double) mc.displayHeight) * ((double) height); + return (int) ret; + } + + protected void centerControls() { + //Determine bounding box + int minX = Integer.MAX_VALUE; + int minY = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int maxY = Integer.MIN_VALUE; + + for(Control ctrl : controls) { + int x = ctrl.getX(); + int y = ctrl.getY(); + if(x < minX) + minX = x; + + if(y < minY) + minY = y; + + x += ctrl.getWidth(); + y += ctrl.getHeight(); + + if(x > maxX) + maxX = x; + + if(y >= maxY) + maxY = y; + } + + //Translation vector + int diffX = (width - maxX - minX) / 2; + int diffY = (height - maxY - minY) / 2; + + //Translate controls + for(Control ctrl : controls) { + int x = ctrl.getX(); + int y = ctrl.getY(); + + ctrl.setPos(x + diffX, y + diffY); + } + } + + @Override + public void drawScreen(int mouseX, int mouseY, float ptt) { + if(defaultBackground) + drawDefaultBackground(); + + for(Control ctrl: controls) + ctrl.draw(mouseX, mouseY, ptt); + } + + @Override + protected void keyTyped(char typedChar, int keyCode) throws IOException { + if(quitOnEscape && keyCode == Keyboard.KEY_ESCAPE) { + mc.displayGuiScreen(null); + return; + } + + for(Control ctrl: controls) + ctrl.keyTyped(typedChar, keyCode); + } + + @Override + protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { + for(Control ctrl: controls) + ctrl.mouseClicked(mouseX, mouseY, mouseButton); + } + + @Override + protected void mouseReleased(int mouseX, int mouseY, int state) { + for(Control ctrl: controls) + ctrl.mouseReleased(mouseX, mouseY, state); + } + + @Override + protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) { + for(Control ctrl: controls) + ctrl.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick); + } + + @Override + public void initGui() { + CURRENT_SCREEN = this; + Keyboard.enableRepeatEvents(true); + } + + @Override + public void onGuiClosed() { + if(syncTicksLeft >= 0) { + sync(); + syncTicksLeft = -1; + } + + for(Control ctrl : controls) + ctrl.destroy(); + + Keyboard.enableRepeatEvents(false); + CURRENT_SCREEN = null; + } + + @Override + public void handleMouseInput() throws IOException { + super.handleMouseInput(); + + int x = Mouse.getEventX() * width / mc.displayWidth; + int y = height - Mouse.getEventY() * height / mc.displayHeight - 1; + int dw = Mouse.getEventDWheel(); + + if(dw != 0) + onMouseScroll(x, y, dw); + else if(Mouse.getEventButton() == -1) + onMouseMove(x, y); + } + + @Override + public void handleKeyboardInput() throws IOException { + super.handleKeyboardInput(); + + int key = Keyboard.getEventKey(); + if(key != Keyboard.KEY_NONE) { + if(Keyboard.getEventKeyState()) { + for(Control ctrl : controls) + ctrl.keyDown(key); + } else { + for(Control ctrl : controls) + ctrl.keyUp(key); + } + } + } + + public void onMouseScroll(int mouseX, int mouseY, int amount) { + for(Control ctrl : controls) + ctrl.mouseScroll(mouseX, mouseY, amount); + } + + public void onMouseMove(int mouseX, int mouseY) { + for(Control ctrl : controls) + ctrl.mouseMove(mouseX, mouseY); + } + + public Object actionPerformed(Event ev) { + Method m = eventMap.get(ev.getClass()); + + if(m != null) { + try { + return m.invoke(this, ev); + } catch(IllegalAccessException e) { + Log.errorEx("Access to event %s of screen %s is denied", e, ev.getClass().getSimpleName(), getClass().getSimpleName()); + } catch(InvocationTargetException e) { + Log.errorEx("Event %s of screen %s failed", e, ev.getClass().getSimpleName(), getClass().getSimpleName()); + } + } + + return null; + } + + public T getControlByName(String name) { + for(Control ctrl : controls) { + if(name.equals(ctrl.getName())) + return (T) ctrl; + + if(ctrl instanceof Container) { + Control ret = ((Container) ctrl).getByName(name); + + if(ret != null) + return (T) ret; + } + } + + return null; + } + + protected void addLoadCustomVariables(Map vars) { + } + + public void loadFrom(ResourceLocation resLoc) { + JsonObject root = GuiLoader.getJson(resLoc); + if(root == null) + throw new RuntimeException("Could not load GUI file " + resLoc.toString()); + + if(!root.has("controls") || !root.get("controls").isJsonArray()) + throw new RuntimeException("In GUI file " + resLoc.toString() + ": missing root 'controls' object."); + + HashMap vars = new HashMap(); + vars.put("width", (double) width); + vars.put("height", (double) height); + vars.put("displayWidth", (double) mc.displayWidth); + vars.put("displayHeight", (double) mc.displayHeight); + addLoadCustomVariables(vars); + + JsonArray content = root.get("controls").getAsJsonArray(); + for(JsonElement elem: content) + controls.add(GuiLoader.create(new JsonOWrapper(elem.getAsJsonObject(), vars))); + + Field[] fields = getClass().getDeclaredFields(); + for(Field f: fields) { + f.setAccessible(true); + FillControl fc = f.getAnnotation(FillControl.class); + + if(fc != null) { + String name = fc.name().isEmpty() ? f.getName() : fc.name(); + Control ctrl = getControlByName(name); + + if(ctrl == null) { + if(fc.required()) + throw new RuntimeException("In GUI file " + resLoc.toString() + ": missing required control " + name); + + continue; + } + + if(!f.getType().isAssignableFrom(ctrl.getClass())) + throw new RuntimeException("In GUI file " + resLoc.toString() + ": invalid type for control " + name); + + try { + f.set(this, ctrl); + } catch(IllegalAccessException e) { + if(fc.required()) + throw new RuntimeException(e); + } + } + } + + if(root.has("center") && root.get("center").getAsBoolean()) + centerControls(); + } + + @Override + public void onResize(Minecraft mcIn, int w, int h) { + for(Control ctrl : controls) + ctrl.destroy(); + + controls.clear(); + super.onResize(mcIn, w, h); + } + + protected void requestAutocomplete(String beginning, boolean matchExact) { + WebDisplays.NET_HANDLER.sendToServer(new SMessageACQuery(beginning, matchExact)); + } + + public void onAutocompleteResult(NameUUIDPair pairs[]) { + } + + public void onAutocompleteFailure() { + } + + protected void requestSync() { + syncTicksLeft = syncTicks - 1; + } + + protected boolean syncRequested() { + return syncTicksLeft >= 0; + } + + protected void abortSync() { + syncTicksLeft = -1; + } + + protected void sync() { + } + + @Override + public void updateScreen() { + if(syncTicksLeft >= 0) { + if(--syncTicksLeft < 0) + sync(); + } + } + +} diff --git a/src/main/java/net/montoyo/wd/client/gui/controls/BasicControl.java b/src/main/java/net/montoyo/wd/client/gui/controls/BasicControl.java new file mode 100644 index 0000000..7d886e9 --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/gui/controls/BasicControl.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client.gui.controls; + +import net.montoyo.wd.client.gui.loading.JsonOWrapper; + +public abstract class BasicControl extends Control { + + protected int x; + protected int y; + protected boolean visible = true; + protected boolean disabled = false; + + @Override + public int getX() { + return x; + } + + @Override + public int getY() { + return y; + } + + @Override + public void setPos(int x, int y) { + this.x = x; + this.y = y; + } + + public boolean isDisabled() { + return disabled; + } + + public void setDisabled(boolean disabled) { + this.disabled = disabled; + } + + public void enable() { + disabled = false; + } + + public void disable() { + disabled = true; + } + + public boolean isVisible() { + return visible; + } + + public void setVisible(boolean visible) { + this.visible = visible; + } + + public void show() { + visible = true; + } + + public void hide() { + visible = false; + } + + @Override + public void load(JsonOWrapper json) { + super.load(json); + x = json.getInt("x", 0); + y = json.getInt("y", 0); + disabled = json.getBool("disabled", false); + visible = json.getBool("visible", true); + } + +} diff --git a/src/main/java/net/montoyo/wd/client/gui/controls/Button.java b/src/main/java/net/montoyo/wd/client/gui/controls/Button.java new file mode 100644 index 0000000..ad45afd --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/gui/controls/Button.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client.gui.controls; + +import net.minecraft.client.gui.GuiButton; +import net.montoyo.wd.client.gui.loading.JsonOWrapper; +import org.lwjgl.input.Keyboard; + +import java.io.IOException; + +public class Button extends Control { + + private final GuiButton btn; + private boolean selected = false; + private boolean shiftDown = false; + private int originalColor = 0; + private int shiftColor = 0; + + public static class ClickEvent extends Event