diff --git a/src/main/java/net/montoyo/wd/SharedProxy.java b/src/main/java/net/montoyo/wd/SharedProxy.java index 967b5cc..401f5e6 100644 --- a/src/main/java/net/montoyo/wd/SharedProxy.java +++ b/src/main/java/net/montoyo/wd/SharedProxy.java @@ -91,7 +91,11 @@ public class SharedProxy { public void setMiniservClientPort(int port) { } - public void startMiniServClient() { + public void startMiniservClient() { + } + + public boolean isMiniservDisabled() { + return false; } } diff --git a/src/main/java/net/montoyo/wd/WebDisplays.java b/src/main/java/net/montoyo/wd/WebDisplays.java index 536bc8a..2e218fc 100644 --- a/src/main/java/net/montoyo/wd/WebDisplays.java +++ b/src/main/java/net/montoyo/wd/WebDisplays.java @@ -82,6 +82,7 @@ public class WebDisplays { public SoundEvent soundUpgradeAdd; public SoundEvent soundUpgradeDel; public SoundEvent soundScreenCfg; + public SoundEvent soundServer; //Criterions public Criterion criterionPadBreak; @@ -176,6 +177,7 @@ public class WebDisplays { soundUpgradeAdd = registerSound(ev, "upgradeAdd"); soundUpgradeDel = registerSound(ev, "upgradeDel"); soundScreenCfg = registerSound(ev, "screencfgOpen"); + soundServer = registerSound(ev, "server"); } @SubscribeEvent diff --git a/src/main/java/net/montoyo/wd/client/ClientProxy.java b/src/main/java/net/montoyo/wd/client/ClientProxy.java index 886834c..6ffce9d 100644 --- a/src/main/java/net/montoyo/wd/client/ClientProxy.java +++ b/src/main/java/net/montoyo/wd/client/ClientProxy.java @@ -309,7 +309,7 @@ public class ClientProxy extends SharedProxy implements IResourceManagerReloadLi } @Override - public void startMiniServClient() { + public void startMiniservClient() { if(miniservPort <= 0) { Log.warning("Can't start miniserv client: miniserv is disabled"); return; @@ -331,6 +331,11 @@ public class ClientProxy extends SharedProxy implements IResourceManagerReloadLi msClientStarted = true; } + @Override + public boolean isMiniservDisabled() { + return miniservPort <= 0; + } + /**************************************** RESOURCE MANAGER METHODS ****************************************/ @Override diff --git a/src/main/java/net/montoyo/wd/client/gui/CommandHandler.java b/src/main/java/net/montoyo/wd/client/gui/CommandHandler.java new file mode 100644 index 0000000..ed4f299 --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/gui/CommandHandler.java @@ -0,0 +1,18 @@ +/* + * 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 CommandHandler { + + String value(); + +} diff --git a/src/main/java/net/montoyo/wd/client/gui/GuiServer.java b/src/main/java/net/montoyo/wd/client/gui/GuiServer.java new file mode 100644 index 0000000..79dcbc8 --- /dev/null +++ b/src/main/java/net/montoyo/wd/client/gui/GuiServer.java @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.client.gui; + +import net.minecraft.client.audio.ISound; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.resources.I18n; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundCategory; +import net.montoyo.wd.WebDisplays; +import net.montoyo.wd.miniserv.client.Client; +import net.montoyo.wd.miniserv.client.ClientTask; +import net.montoyo.wd.miniserv.client.ClientTaskGetFileList; +import net.montoyo.wd.miniserv.client.ClientTaskGetQuota; +import net.montoyo.wd.utilities.Log; +import net.montoyo.wd.utilities.NameUUIDPair; +import net.montoyo.wd.utilities.Util; +import org.lwjgl.input.Keyboard; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +import static org.lwjgl.opengl.GL11.*; + +public class GuiServer extends WDScreen { + + private static final ResourceLocation BG_IMAGE = new ResourceLocation("webdisplays", "textures/gui/server_bg.png"); + private static final ResourceLocation FG_IMAGE = new ResourceLocation("webdisplays", "textures/gui/server_fg.png"); + private static final HashMap COMMAND_MAP = new HashMap<>(); + + private final NameUUIDPair owner; + private final ArrayList lines = new ArrayList<>(); + private String prompt = ""; + private String userPrompt; + private int blinkTime; + private String lastCmd; + private boolean promptLocked; + private long queryTime; + private ClientTask currentTask; + + //Access command + private int accessTrials; + private int accessTime; + private int accessState = -1; + private PositionedSoundRecord accessSound; + + public GuiServer(NameUUIDPair owner) { + this.owner = owner; + //userPrompt = owner.name + "@miniserv$ "; + userPrompt = "> "; + + if(COMMAND_MAP.isEmpty()) + buildCommandMap(); + + lines.add("MiniServ 1.0"); + lines.add(tr("info")); + } + + private static String tr(String key, Object ... args) { + return I18n.format("webdisplays.server." + key, args); + } + + @Override + public void drawScreen(int mouseX, int mouseY, float ptt) { + super.drawScreen(mouseX, mouseY, ptt); + + int x = (width - 256) / 2; + int y = (height - 176) / 2; + + GlStateManager.enableTexture2D(); + mc.renderEngine.bindTexture(BG_IMAGE); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + drawTexturedModalRect(x, y, 0, 0, 256, 176); + + x += 18; + y += 18; + + for(String line: lines) { + fontRenderer.drawString(line, x, y, 0xFFFFFFFF, false); + y += 12; + } + + if(!promptLocked) { + x = fontRenderer.drawString(userPrompt, x, y, 0xFFFFFFFF, false); + x = fontRenderer.drawString(prompt, x, y, 0xFFFFFFFF, false); + } + + if(blinkTime < 5) { + double xd = (double) (x + 1); + double yd = (double) y; + double zd = (double) zLevel; + + GlStateManager.disableTexture2D(); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + Tessellator t = Tessellator.getInstance(); + BufferBuilder bb = t.getBuffer(); + bb.begin(GL_QUADS, DefaultVertexFormats.POSITION); + bb.pos(xd, yd + 8.0f, zd).endVertex(); + bb.pos(xd + 6.0f, yd + 8.0f, zd).endVertex(); + bb.pos(xd + 6.0f, yd, zd).endVertex(); + bb.pos(xd, yd, zd).endVertex(); + t.draw(); + } + + GlStateManager.disableAlpha(); + GlStateManager.enableTexture2D(); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + mc.renderEngine.bindTexture(FG_IMAGE); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + drawTexturedModalRect((width - 256) / 2, (height - 176) / 2, 0, 0, 256, 176); + } + + @Override + public void updateScreen() { + super.updateScreen(); + + if(accessState >= 0) { + if(--accessTime <= 0) { + accessState++; + + if(accessState == 1) { + if(lines.size() > 0) + lines.remove(lines.size() - 1); + + lines.add("access: PERMISSION DENIED....and..."); + accessTime = 20; + } else { + if(accessSound == null) { + accessSound = new PositionedSoundRecord(WebDisplays.INSTANCE.soundServer.getSoundName(), SoundCategory.MASTER, 1.0f, 1.0f, true, 0, ISound.AttenuationType.NONE, 0.0f, 0.0f, 0.0f); + mc.getSoundHandler().playSound(accessSound); + } + + writeLine("YOU DIDN'T SAY THE MAGIC WORD!"); + accessTime = 2; + } + } + } else { + blinkTime = (blinkTime + 1) % 10; + + if(currentTask != null && System.currentTimeMillis() - queryTime >= 10000) { + writeLine(tr("timeout")); + currentTask.cancel(); + clearTask(); + } + } + } + + @Override + public void handleKeyboardInput() throws IOException { + if(!promptLocked && Keyboard.getEventKeyState() && Keyboard.getEventKey() == Keyboard.KEY_UP) { + if(lastCmd != null) + prompt = lastCmd; + + return; + } + + super.handleKeyboardInput(); + + if(Keyboard.getEventKeyState() && Keyboard.getEventKey() == Keyboard.KEY_L && (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL))) + lines.clear(); + } + + @Override + protected void keyTyped(char typedChar, int keyCode) throws IOException { + super.keyTyped(typedChar, keyCode); + + if(promptLocked) + return; + + if(keyCode == Keyboard.KEY_BACK) { + if(prompt.length() > 0) + prompt = prompt.substring(0, prompt.length() - 1); + } else if(keyCode == Keyboard.KEY_RETURN || keyCode == Keyboard.KEY_NUMPADENTER) { + if(prompt.length() > 0) { + writeLine(userPrompt + prompt); + evaluateCommand(prompt); + lastCmd = prompt; + prompt = ""; + } else + writeLine(userPrompt); + } else if(prompt.length() + 1 < 30 && typedChar >= 32 && typedChar <= 126) + prompt = prompt + typedChar; + + blinkTime = 0; + } + + private void evaluateCommand(String str) { + String[] args = str.trim().split("\\s+"); + Method handler = COMMAND_MAP.get(args[0].toLowerCase()); + + if(handler == null) { + writeLine(tr("unknowncmd")); + return; + } + + Object[] params; + if(handler.getParameterCount() == 0) + params = new Object[0]; + else { + String[] args2 = new String[args.length - 1]; + System.arraycopy(args, 1, args2, 0, args2.length); + params = new Object[] { args2 }; + } + + try { + handler.invoke(this, params); + } catch(IllegalAccessException | InvocationTargetException e) { + Log.errorEx("Caught exception while running command \"%s\"", e, str); + writeLine(tr("error")); + } + } + + private void writeLine(String line) { + while(lines.size() >= 11) + lines.remove(0); + + lines.add(line); + } + + private static void buildCommandMap() { + COMMAND_MAP.clear(); + + Method[] methods = GuiServer.class.getMethods(); + for(Method m: methods) { + CommandHandler cmd = m.getAnnotation(CommandHandler.class); + + if(cmd != null && Modifier.isPublic(m.getModifiers())) { + if(m.getParameterCount() == 0 || (m.getParameterCount() == 1 && m.getParameterTypes()[0] == String[].class)) + COMMAND_MAP.put(cmd.value().toLowerCase(), m); + } + } + } + + @Override + public void onGuiClosed() { + super.onGuiClosed(); + + if(accessSound != null) + mc.getSoundHandler().stopSound(accessSound); + } + + private boolean queueTask(ClientTask task) { + if(Client.getInstance().addTask(task)) { + promptLocked = true; + queryTime = System.currentTimeMillis(); + currentTask = task; + return true; + } else { + writeLine(tr("queryerr")); + return false; + } + } + + private void clearTask() { + promptLocked = false; + currentTask = null; + } + + @CommandHandler("clear") + public void commandClear() { + lines.clear(); + } + + @CommandHandler("help") + public void commandHelp() { + for(String c : COMMAND_MAP.keySet()) + writeLine(c + " - " + tr("help." + c)); + } + + @CommandHandler("exit") + public void commandExit() { + mc.displayGuiScreen(null); + } + + @CommandHandler("access") + public void commandAccess(String[] args) { + boolean handled = false; + + if(args.length >= 1 && args[0].equalsIgnoreCase("security")) { + if(args.length == 1 || (args.length == 2 && args[1].equalsIgnoreCase("grid"))) + handled = true; + } else if(args.length == 3 && args[0].equalsIgnoreCase("main") && args[1].equalsIgnoreCase("security") && args[2].equalsIgnoreCase("grid")) + handled = true; + + if(handled) { + writeLine("access: PERMISSION DENIED."); + + if(++accessTrials >= 3) { + promptLocked = true; + accessState = 0; + accessTime = 20; + } + } else + writeLine(tr("argerror")); + } + + @CommandHandler("owner") + public void commandOwner() { + writeLine(tr("ownername", owner.name)); + writeLine(tr("owneruuid")); + writeLine(owner.uuid.toString()); + } + + @CommandHandler("quota") + public void commandQuota() { + if(!mc.player.getGameProfile().getId().equals(owner.uuid)) { + writeLine(tr("errowner")); + return; + } + + ClientTaskGetQuota task = new ClientTaskGetQuota(); + task.setFinishCallback((t) -> { + writeLine(tr("quota", Util.sizeString(t.getQuota()), Util.sizeString(t.getMaxQuota()))); + clearTask(); + }); + + queueTask(task); + } + + @CommandHandler("ls") + public void commandList() { + ClientTaskGetFileList task = new ClientTaskGetFileList(owner.uuid); + task.setFinishCallback((t) -> { + String[] files = t.getFileList(); + if(files != null) + Arrays.stream(files).forEach(this::writeLine); + + clearTask(); + }); + + queueTask(task); + } + +} diff --git a/src/main/java/net/montoyo/wd/client/gui/controls/CheckBox.java b/src/main/java/net/montoyo/wd/client/gui/controls/CheckBox.java index ab7d4ae..ab98abf 100644 --- a/src/main/java/net/montoyo/wd/client/gui/controls/CheckBox.java +++ b/src/main/java/net/montoyo/wd/client/gui/controls/CheckBox.java @@ -5,6 +5,7 @@ package net.montoyo.wd.client.gui.controls; import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.renderer.GlStateManager; import net.minecraft.init.SoundEvents; import net.minecraft.util.ResourceLocation; import net.montoyo.wd.client.gui.loading.JsonOWrapper; @@ -71,6 +72,8 @@ public class CheckBox extends BasicControl { @Override public void draw(int mouseX, int mouseY, float ptt) { if(visible) { + GlStateManager.disableAlpha(); + bindTexture(checked ? texChecked : texUnchecked); blend(true); fillTexturedRect(x, y, WIDTH, HEIGHT, 0.0, 0.0, 1.0, 1.0); diff --git a/src/main/java/net/montoyo/wd/data/GuiData.java b/src/main/java/net/montoyo/wd/data/GuiData.java index ef355a7..afa0632 100644 --- a/src/main/java/net/montoyo/wd/data/GuiData.java +++ b/src/main/java/net/montoyo/wd/data/GuiData.java @@ -22,6 +22,7 @@ public abstract class GuiData { dataTable.put("ScreenConfig", ScreenConfigData.class); dataTable.put("Keyboard", KeyboardData.class); dataTable.put("RedstoneCtrl", RedstoneCtrlData.class); + dataTable.put("Server", ServerData.class); } public static Class classOf(String name) { diff --git a/src/main/java/net/montoyo/wd/data/ServerData.java b/src/main/java/net/montoyo/wd/data/ServerData.java new file mode 100644 index 0000000..b23be03 --- /dev/null +++ b/src/main/java/net/montoyo/wd/data/ServerData.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.data; + +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import net.montoyo.wd.client.gui.GuiServer; +import net.montoyo.wd.utilities.NameUUIDPair; + +public class ServerData extends GuiData { + + public NameUUIDPair owner; + + public ServerData() { + } + + public ServerData(NameUUIDPair owner) { + this.owner = owner; + } + + @SideOnly(Side.CLIENT) + @Override + public GuiScreen createGui(GuiScreen old, World world) { + return new GuiServer(owner); + } + + @Override + public String getName() { + return "Server"; + } + +} diff --git a/src/main/java/net/montoyo/wd/entity/TileEntityServer.java b/src/main/java/net/montoyo/wd/entity/TileEntityServer.java index 67a38da..f3ae578 100644 --- a/src/main/java/net/montoyo/wd/entity/TileEntityServer.java +++ b/src/main/java/net/montoyo/wd/entity/TileEntityServer.java @@ -5,9 +5,12 @@ package net.montoyo.wd.entity; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; +import net.montoyo.wd.data.ServerData; import net.montoyo.wd.utilities.NameUUIDPair; +import net.montoyo.wd.utilities.Util; import javax.annotation.Nonnull; import java.util.UUID; @@ -49,6 +52,11 @@ public class TileEntityServer extends TileEntity { if(world.isRemote) return true; + //TODO: Check if miniserv is disabled + //Util.toast(ply, "noMiniserv"); + + if(owner != null && ply instanceof EntityPlayerMP) + (new ServerData(owner)).sendTo((EntityPlayerMP) ply); return true; } diff --git a/src/main/java/net/montoyo/wd/item/ItemPeripheral.java b/src/main/java/net/montoyo/wd/item/ItemPeripheral.java index 470668a..ac87227 100644 --- a/src/main/java/net/montoyo/wd/item/ItemPeripheral.java +++ b/src/main/java/net/montoyo/wd/item/ItemPeripheral.java @@ -16,6 +16,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3i; import net.minecraft.world.World; +import net.montoyo.wd.WebDisplays; import net.montoyo.wd.block.BlockKeyboardRight; import net.montoyo.wd.core.DefaultPeripheral; @@ -53,6 +54,8 @@ public class ItemPeripheral extends ItemMultiTexture { public void addInformation(ItemStack is, @Nullable World world, List tt, ITooltipFlag ttFlags) { if(is.getMetadata() == 1) //CC Interface tt.add("" + ChatFormatting.RED + I18n.format("webdisplays.message.missingCC")); //CC is not available for 1.12.2 + else if(is.getMetadata() == 11 && WebDisplays.PROXY.isMiniservDisabled()) //Server + tt.add("" + ChatFormatting.RED + I18n.format("webdisplays.message.noMiniserv")); } } diff --git a/src/main/java/net/montoyo/wd/miniserv/PacketID.java b/src/main/java/net/montoyo/wd/miniserv/PacketID.java index f076404..569b5f7 100644 --- a/src/main/java/net/montoyo/wd/miniserv/PacketID.java +++ b/src/main/java/net/montoyo/wd/miniserv/PacketID.java @@ -12,7 +12,9 @@ public enum PacketID { BEGIN_FILE_UPLOAD, //C->S FILE_PART, //C->S and S->C FILE_STATUS, //S->C - GET_FILE; //C->S + GET_FILE, //C->S + QUOTA, //C->S and S->C + LIST; //C->S and S->C public static PacketID fromInt(int i) { PacketID[] values = values(); diff --git a/src/main/java/net/montoyo/wd/miniserv/client/Client.java b/src/main/java/net/montoyo/wd/miniserv/client/Client.java index 3a69d5b..7f02c47 100644 --- a/src/main/java/net/montoyo/wd/miniserv/client/Client.java +++ b/src/main/java/net/montoyo/wd/miniserv/client/Client.java @@ -216,7 +216,7 @@ public class Client extends AbstractClient implements Runnable { private void unsafeLoop() throws Throwable { selector.select(); - if(currentTask == null) + if(currentTask == null || currentTask.isCanceled()) nextTask(); for(SelectionKey key: selector.selectedKeys()) { @@ -297,6 +297,27 @@ public class Client extends AbstractClient implements Runnable { } } + @PacketHandler(PacketID.QUOTA) + public void handleQuota(DataInputStream dis) throws IOException { + long q = dis.readLong(); + long m = dis.readLong(); + + if(currentTask instanceof ClientTaskGetQuota) + ((ClientTaskGetQuota) currentTask).onQuotaData(q, m); + } + + @PacketHandler(PacketID.LIST) + public void handleList(DataInputStream dis) throws IOException { + int cnt = dis.readByte() & 0xFF; + String[] files = new String[cnt]; + + for(int i = 0; i < cnt; i++) + files[i] = readString(dis); + + if(currentTask instanceof ClientTaskGetFileList) + ((ClientTaskGetFileList) currentTask).onFileList(files); + } + public void nextTask() { if(currentTask != null) currentTask.onFinished(); @@ -326,4 +347,14 @@ public class Client extends AbstractClient implements Runnable { return true; } + public void wakeup() { + boolean conn; + synchronized(this) { + conn = connected; + } + + if(conn) + selector.wakeup(); + } + } diff --git a/src/main/java/net/montoyo/wd/miniserv/client/ClientTask.java b/src/main/java/net/montoyo/wd/miniserv/client/ClientTask.java index 4554bfa..dc0160e 100644 --- a/src/main/java/net/montoyo/wd/miniserv/client/ClientTask.java +++ b/src/main/java/net/montoyo/wd/miniserv/client/ClientTask.java @@ -4,11 +4,15 @@ package net.montoyo.wd.miniserv.client; +import net.montoyo.wd.WebDisplays; + import java.util.function.Consumer; -public abstract class ClientTask { +public abstract class ClientTask { - private Consumer finishCallback; + private Consumer finishCallback; + private volatile boolean canceled; + protected boolean runCallbackOnMcThread; protected final Client client = Client.getInstance(); public abstract void start(); @@ -16,12 +20,37 @@ public abstract class ClientTask { public void onFinished() { //Called by Client, don't call it from a ClientTask! - if(finishCallback != null) - finishCallback.accept(this); + if(finishCallback != null && !isCanceled()) { + if(runCallbackOnMcThread) + WebDisplays.PROXY.enqueue(() -> finishCallback.accept((T) this)); + else + finishCallback.accept((T) this); + } } - public void setFinishCallback(Consumer finishCallback) { + public void setFinishCallback(Consumer finishCallback) { this.finishCallback = finishCallback; } + public void setRunCallbackOnMinecraftThread(boolean runCallbackOnMcThread) { + this.runCallbackOnMcThread = runCallbackOnMcThread; + } + + public final void cancel() { + synchronized(this) { + canceled = true; + } + + Client.getInstance().wakeup(); + } + + public final boolean isCanceled() { + boolean ret; + synchronized(this) { + ret = canceled; + } + + return ret; + } + } diff --git a/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskGetFile.java b/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskGetFile.java index ce28fb1..b94e7f4 100644 --- a/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskGetFile.java +++ b/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskGetFile.java @@ -13,7 +13,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; -public class ClientTaskGetFile extends ClientTask { +public class ClientTaskGetFile extends ClientTask { private final UUID uuid; private final String fname; @@ -86,6 +86,7 @@ public class ClientTaskGetFile extends ClientTask { while(!hasResponse) { if(System.currentTimeMillis() - t > 10000) { responseLock.unlock(); + cancel(); return Constants.GETF_STATUS_TIMED_OUT; } @@ -124,6 +125,7 @@ public class ClientTaskGetFile extends ClientTask { data = new byte[0]; dataLen = -1; dataLock.unlock(); + cancel(); return data; } diff --git a/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskGetFileList.java b/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskGetFileList.java new file mode 100644 index 0000000..9e8880c --- /dev/null +++ b/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskGetFileList.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.miniserv.client; + +import net.montoyo.wd.miniserv.OutgoingPacket; +import net.montoyo.wd.miniserv.PacketID; + +import java.util.UUID; + +public class ClientTaskGetFileList extends ClientTask { + + private final UUID user; + private String[] files; + + public ClientTaskGetFileList(UUID user) { + this.user = user; + runCallbackOnMcThread = true; + } + + @Override + public void start() { + OutgoingPacket pkt = new OutgoingPacket(); + pkt.writeByte(PacketID.LIST.ordinal()); + pkt.writeLong(user.getMostSignificantBits()); + pkt.writeLong(user.getLeastSignificantBits()); + client.sendPacket(pkt); + } + + @Override + public void abort() { + } + + public void onFileList(String[] files) { + this.files = files; + client.nextTask(); + } + + public String[] getFileList() { + return files; + } + +} diff --git a/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskGetQuota.java b/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskGetQuota.java new file mode 100644 index 0000000..fc5631a --- /dev/null +++ b/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskGetQuota.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 BARBOTIN Nicolas + */ + +package net.montoyo.wd.miniserv.client; + +import net.montoyo.wd.miniserv.OutgoingPacket; +import net.montoyo.wd.miniserv.PacketID; + +public class ClientTaskGetQuota extends ClientTask { + + private long quota; + private long maxQuota; + + public ClientTaskGetQuota() { + runCallbackOnMcThread = true; + } + + @Override + public void start() { + OutgoingPacket pkt = new OutgoingPacket(); + pkt.writeByte(PacketID.QUOTA.ordinal()); + client.sendPacket(pkt); + } + + @Override + public void abort() { + } + + public void onQuotaData(long q, long m) { + quota = q; + maxQuota = m; + client.nextTask(); + } + + public long getMaxQuota() { + return maxQuota; + } + + public long getQuota() { + return quota; + } + +} diff --git a/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskUploadFile.java b/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskUploadFile.java index 7621ea5..5cc2f9e 100644 --- a/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskUploadFile.java +++ b/src/main/java/net/montoyo/wd/miniserv/client/ClientTaskUploadFile.java @@ -16,7 +16,7 @@ import java.io.IOException; import java.nio.file.Files; import java.util.function.Consumer; -public class ClientTaskUploadFile extends ClientTask implements Consumer { +public class ClientTaskUploadFile extends ClientTask implements Consumer { private static final byte[] UPLOAD_BUFFER = new byte[65535]; diff --git a/src/main/java/net/montoyo/wd/miniserv/server/ServerClient.java b/src/main/java/net/montoyo/wd/miniserv/server/ServerClient.java index d2ee633..babae53 100644 --- a/src/main/java/net/montoyo/wd/miniserv/server/ServerClient.java +++ b/src/main/java/net/montoyo/wd/miniserv/server/ServerClient.java @@ -13,6 +13,7 @@ import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; +import java.util.Arrays; import java.util.UUID; import java.util.function.Consumer; @@ -236,6 +237,43 @@ public class ServerClient extends AbstractClient { } } + @PacketHandler(PacketID.QUOTA) + public void handleQuota(DataInputStream dis) { + OutgoingPacket pkt = new OutgoingPacket(); + pkt.writeByte(PacketID.QUOTA.ordinal()); + pkt.writeLong(quota); + pkt.writeLong(Server.getInstance().getMaxQuota()); + + sendPacket(pkt); + } + + @PacketHandler(PacketID.LIST) + public void handleList(DataInputStream dis) throws IOException { + long msb = dis.readLong(); + long lsb = dis.readLong(); + File dir = new File(Server.getInstance().getDirectory(), (new UUID(msb, lsb)).toString()); + String[] list = null; + + if(dir.exists() && dir.isDirectory()) { + File[] files = dir.listFiles(); + + if(files != null) + list = Arrays.stream(files).filter(f -> f.isFile() && !Util.isFileNameInvalid(f.getName())).map(File::getName).toArray(String[]::new); + } + + OutgoingPacket pkt = new OutgoingPacket(); + pkt.writeByte(PacketID.LIST.ordinal()); + + if(list == null) + pkt.writeByte(0); + else { + pkt.writeByte(list.length); + Arrays.stream(list).forEach(pkt::writeString); + } + + sendPacket(pkt); + } + private void finishUpload(int status) { if(currentFile != null) { OutgoingPacket pkt = new OutgoingPacket(); diff --git a/src/main/java/net/montoyo/wd/net/client/CMessageMiniservKey.java b/src/main/java/net/montoyo/wd/net/client/CMessageMiniservKey.java index fbd5568..5cca09a 100644 --- a/src/main/java/net/montoyo/wd/net/client/CMessageMiniservKey.java +++ b/src/main/java/net/montoyo/wd/net/client/CMessageMiniservKey.java @@ -40,7 +40,7 @@ public class CMessageMiniservKey implements IMessage, Runnable { public void run() { if(Client.getInstance().decryptKey(encryptedKey)) { Log.info("Successfully received and decrypted key, starting miniserv client..."); - WebDisplays.PROXY.startMiniServClient(); + WebDisplays.PROXY.startMiniservClient(); } } diff --git a/src/main/java/net/montoyo/wd/utilities/Util.java b/src/main/java/net/montoyo/wd/utilities/Util.java index 4703a81..32d71e9 100644 --- a/src/main/java/net/montoyo/wd/utilities/Util.java +++ b/src/main/java/net/montoyo/wd/utilities/Util.java @@ -227,4 +227,19 @@ public abstract class Util { public static boolean isFileNameInvalid(String fname) { return fname.isEmpty() || fname.length() > 64 || fname.charAt(0) == '.' || fname.indexOf('/') >= 0 || fname.indexOf('\\') >= 0; } + + public static final String[] SIZES = { "bytes", "KiB", "MiB", "GiB", "TiB" }; + + public static String sizeString(long l) { + double d = (double) l; + int size = 0; + + while(l >= 1024L && size + 1 < SIZES.length) { + d /= 1024.0; + l /= 1024L; + size++; + } + + return String.format("%.2f %s", d, SIZES[size]); + } } diff --git a/src/main/resources/assets/webdisplays/lang/en_us.lang b/src/main/resources/assets/webdisplays/lang/en_us.lang index e770751..113e2a0 100644 --- a/src/main/resources/assets/webdisplays/lang/en_us.lang +++ b/src/main/resources/assets/webdisplays/lang/en_us.lang @@ -50,6 +50,7 @@ webdisplays.message.missingOC=OpenComputers is not available. webdisplays.message.upgradeError=Upgrade error :( Check logs... webdisplays.message.upgradeOk=Upgrade installed! webdisplays.message.linkAbort=Linker reset +webdisplays.message.noMiniserv=Server block is disabled on this server webdisplays.gui.screencfg.owner=Screen owner: webdisplays.gui.screencfg.friends=Friends: webdisplays.gui.screencfg.permissions=Permissions: @@ -110,3 +111,20 @@ webdisplays.side.north=North webdisplays.side.south=South webdisplays.side.west=West webdisplays.side.east=East +webdisplays.server.info=Type "help" if you need some. +webdisplays.server.unknowncmd=Unknown command. +webdisplays.server.error=Internal error. Check logs. +webdisplays.server.argerror=Unrecognized argument. +webdisplays.server.queryerr=Query error, check logs. +webdisplays.server.errowner=Only the owner can access this. +webdisplays.server.timeout=Query timed out. Check logs. +webdisplays.server.ownername=Owner name: %s +webdisplays.server.owneruuid=Owner UUID: +webdisplays.server.quota=%s/%s used +webdisplays.server.help.help=Displays this text +webdisplays.server.help.clear=Clears the screen +webdisplays.server.help.exit=Leaves this console +webdisplays.server.help.access=§kNo help data +webdisplays.server.help.owner=Displays the server owner +webdisplays.server.help.quota=Displays the storage quota +webdisplays.server.help.ls=Lists the files on this server diff --git a/src/main/resources/assets/webdisplays/sounds.json b/src/main/resources/assets/webdisplays/sounds.json index f1f2b2b..68c0910 100644 --- a/src/main/resources/assets/webdisplays/sounds.json +++ b/src/main/resources/assets/webdisplays/sounds.json @@ -29,5 +29,11 @@ "sounds": [ "webdisplays:screencfg_open" ] + }, + "server": { + "category": "master", + "sounds": [ + "webdisplays:server" + ] } } diff --git a/src/main/resources/assets/webdisplays/sounds/server.ogg b/src/main/resources/assets/webdisplays/sounds/server.ogg new file mode 100644 index 0000000..3ba7445 Binary files /dev/null and b/src/main/resources/assets/webdisplays/sounds/server.ogg differ diff --git a/src/main/resources/assets/webdisplays/textures/gui/server_bg.png b/src/main/resources/assets/webdisplays/textures/gui/server_bg.png new file mode 100644 index 0000000..0c1b729 Binary files /dev/null and b/src/main/resources/assets/webdisplays/textures/gui/server_bg.png differ diff --git a/src/main/resources/assets/webdisplays/textures/gui/server_fg.png b/src/main/resources/assets/webdisplays/textures/gui/server_fg.png new file mode 100644 index 0000000..70f935e Binary files /dev/null and b/src/main/resources/assets/webdisplays/textures/gui/server_fg.png differ