From 29926c2deb91f54710168375cbfa39bc625df7a4 Mon Sep 17 00:00:00 2001 From: malken21 <77374813+malken21@users.noreply.github.com> Date: Thu, 28 Mar 2024 19:44:57 +0900 Subject: [PATCH] setup --- .gitignore | 118 +++++++++++++ LICENSE | 21 +++ build.gradle | 85 +++++++++ gradle.properties | 17 ++ gradle/wrapper/gradle-wrapper.properties | 1 + settings.gradle | 9 + .../maruma_sign_webui/MarumaSignWebUI.java | 17 ++ .../maruma_sign_webui/ServerManager.java | 24 +++ .../client/MarumaSignWebUIClient.java | 28 +++ .../maruma_sign_webui/http/ServerEngine.java | 39 +++++ .../http/service/APIService.java | 99 +++++++++++ .../http/service/WebUIService.java | 71 ++++++++ .../maruma_sign_webui/util/PortManager.java | 36 ++++ .../maruma_sign_webui/util/Utils.java | 21 +++ .../assets/maruma_sign_webui/lang/en_us.json | 4 + .../assets/maruma_sign_webui/lang/ja_jp.json | 4 + src/main/resources/fabric.mod.json | 27 +++ src/main/resources/webui/index.html | 60 +++++++ src/main/resources/webui/script.js | 50 ++++++ src/main/resources/webui/style.css | 161 ++++++++++++++++++ src/main/resources/webui/utils.js | 84 +++++++++ 21 files changed, 976 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 settings.gradle create mode 100644 src/main/java/marumasa/maruma_sign_webui/MarumaSignWebUI.java create mode 100644 src/main/java/marumasa/maruma_sign_webui/ServerManager.java create mode 100644 src/main/java/marumasa/maruma_sign_webui/client/MarumaSignWebUIClient.java create mode 100644 src/main/java/marumasa/maruma_sign_webui/http/ServerEngine.java create mode 100644 src/main/java/marumasa/maruma_sign_webui/http/service/APIService.java create mode 100644 src/main/java/marumasa/maruma_sign_webui/http/service/WebUIService.java create mode 100644 src/main/java/marumasa/maruma_sign_webui/util/PortManager.java create mode 100644 src/main/java/marumasa/maruma_sign_webui/util/Utils.java create mode 100644 src/main/resources/assets/maruma_sign_webui/lang/en_us.json create mode 100644 src/main/resources/assets/maruma_sign_webui/lang/ja_jp.json create mode 100644 src/main/resources/fabric.mod.json create mode 100644 src/main/resources/webui/index.html create mode 100644 src/main/resources/webui/script.js create mode 100644 src/main/resources/webui/style.css create mode 100644 src/main/resources/webui/utils.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c37caf --- /dev/null +++ b/.gitignore @@ -0,0 +1,118 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Cache of project +.gradletasknamecache + +**/build/ + +# Common working directory +run/ + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c986429 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2024 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..55a077b --- /dev/null +++ b/build.gradle @@ -0,0 +1,85 @@ +plugins { + id 'fabric-loom' version '1.5-SNAPSHOT' + id 'maven-publish' +} + +version = project.mod_version +group = project.maven_group + +repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + } + +processResources { + inputs.property "version", project.version + inputs.property "minecraft_version", project.minecraft_version + inputs.property "loader_version", project.loader_version + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version, + "minecraft_version": project.minecraft_version, + "loader_version": project.loader_version + } +} + +def targetJavaVersion = 17 +tasks.withType(JavaCompile).configureEach { + // ensure that the encoding is set to UTF-8, no matter what the system default is + // this fixes some edge cases with special characters not displaying correctly + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html + // If Javadoc is generated, this must be specified in that task too. + it.options.encoding = "UTF-8" + if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { + it.options.release.set(targetJavaVersion) + } +} + +java { + def javaVersion = JavaVersion.toVersion(targetJavaVersion) + if (JavaVersion.current() < javaVersion) { + toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) + } + archivesBaseName = project.archives_base_name + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() +} + +jar { + from("LICENSE") { + rename { "${it}_${project.archivesBaseName}"} + } +} + +// configure the maven publication +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..eab5771 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G + +# Fabric Properties + # check these on https://modmuss50.me/fabric.html + minecraft_version=1.20.4 + yarn_mappings=1.20.4+build.3 + loader_version=0.15.7 + +# Mod Properties + mod_version = 1.0 + maven_group = marumasa + archives_base_name = maruma_sign_webui + +# Dependencies + # check this on https://modmuss50.me/fabric.html + fabric_version=0.96.11+1.20.4 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e096528 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..f91a4fe --- /dev/null +++ b/settings.gradle @@ -0,0 +1,9 @@ +pluginManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + gradlePluginPortal() + } +} diff --git a/src/main/java/marumasa/maruma_sign_webui/MarumaSignWebUI.java b/src/main/java/marumasa/maruma_sign_webui/MarumaSignWebUI.java new file mode 100644 index 0000000..43a62cf --- /dev/null +++ b/src/main/java/marumasa/maruma_sign_webui/MarumaSignWebUI.java @@ -0,0 +1,17 @@ +package marumasa.maruma_sign_webui; + +import com.mojang.logging.LogUtils; +import net.fabricmc.api.ModInitializer; +import org.slf4j.Logger; + +public class MarumaSignWebUI implements ModInitializer { + // ModのID + public static final String MOD_ID = "maruma_sign_webui"; + // ロガー + public static final Logger LOGGER = LogUtils.getLogger(); + + @Override + public void onInitialize() { + LOGGER.info("Start: " + MOD_ID); + } +} diff --git a/src/main/java/marumasa/maruma_sign_webui/ServerManager.java b/src/main/java/marumasa/maruma_sign_webui/ServerManager.java new file mode 100644 index 0000000..948898a --- /dev/null +++ b/src/main/java/marumasa/maruma_sign_webui/ServerManager.java @@ -0,0 +1,24 @@ +package marumasa.maruma_sign_webui; + +import marumasa.maruma_sign_webui.util.PortManager; +import marumasa.maruma_sign_webui.http.ServerEngine; +import net.minecraft.util.Util; + +public class ServerManager { + + private static ServerEngine engine; + + public static void openMenu() { + if (engine == null) // サーバーが起動していない場合 + build(); // サーバーを起動 + // ブラウザで起動したサーバーに接続する + Util.getOperatingSystem().open(String.format("http://localhost:%d%s", engine.port, "/index.html")); + } + + private static void build() { + // ポート番号を設定 + int port = PortManager.generate(); + // サーバーを起動させる + engine = new ServerEngine(port); + } +} diff --git a/src/main/java/marumasa/maruma_sign_webui/client/MarumaSignWebUIClient.java b/src/main/java/marumasa/maruma_sign_webui/client/MarumaSignWebUIClient.java new file mode 100644 index 0000000..b2d379f --- /dev/null +++ b/src/main/java/marumasa/maruma_sign_webui/client/MarumaSignWebUIClient.java @@ -0,0 +1,28 @@ +package marumasa.maruma_sign_webui.client; + +import marumasa.maruma_sign_webui.ServerManager; +import marumasa.maruma_sign_webui.util.Utils; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.minecraft.client.option.KeyBinding; +import org.lwjgl.glfw.GLFW; + +public class MarumaSignWebUIClient implements ClientModInitializer { + @Override + public void onInitializeClient() { + + // キーバインド登録 + KeyBinding binding = KeyBindingHelper.registerKeyBinding( + Utils.createKeyBinding("open_menu", GLFW.GLFW_KEY_M) + ); + + // クライアントティックイベントにキーバインドの処理を登録 + ClientTickEvents.END_CLIENT_TICK.register(client -> { + // もしキーが押されたら + while (binding.wasPressed()) { + ServerManager.openMenu(); + } + }); + } +} diff --git a/src/main/java/marumasa/maruma_sign_webui/http/ServerEngine.java b/src/main/java/marumasa/maruma_sign_webui/http/ServerEngine.java new file mode 100644 index 0000000..cb3ef3b --- /dev/null +++ b/src/main/java/marumasa/maruma_sign_webui/http/ServerEngine.java @@ -0,0 +1,39 @@ +package marumasa.maruma_sign_webui.http; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import marumasa.maruma_sign_webui.http.service.APIService; +import marumasa.maruma_sign_webui.http.service.WebUIService; + +import java.io.IOException; +import java.net.InetSocketAddress; + +public class ServerEngine { + + public final HttpServer server; + public final int port; + + public ServerEngine(int port) { + this.port = port; + try { + server = HttpServer.create(new InetSocketAddress(port), 0); + server.start(); + } catch (IOException e) { + throw new RuntimeException(e); + } + server.createContext("/", new Handler()); + } + + public static class Handler implements HttpHandler { + @Override + public void handle(HttpExchange exchange) throws IOException { + // リクエストを処理する + if ("POST".equals(exchange.getRequestMethod())) { + APIService.Handle(exchange); + } else { + WebUIService.Handle(exchange); + } + } + } +} diff --git a/src/main/java/marumasa/maruma_sign_webui/http/service/APIService.java b/src/main/java/marumasa/maruma_sign_webui/http/service/APIService.java new file mode 100644 index 0000000..5d9b9d4 --- /dev/null +++ b/src/main/java/marumasa/maruma_sign_webui/http/service/APIService.java @@ -0,0 +1,99 @@ +package marumasa.maruma_sign_webui.http.service; + +import com.google.gson.Gson; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayNetworkHandler; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class APIService { + + public static void Handle(HttpExchange exchange) throws IOException { + + // リクエストボディ取得 + String reqBody = new String(exchange.getRequestBody().readAllBytes()); + // リクエストボディに書かれている json を解析 + RequestJson json = gson.fromJson(reqBody, RequestJson.class); + // 看板に書かれる文字を生成 + String signText = json.signText(); + // レスポンスボディを設定 + String resBody = gson.toJson(new ResponseJson(signText)); + + // レスポンスヘッダを設定 + Headers responseHeaders = exchange.getResponseHeaders(); + responseHeaders.set("Content-Type", "application/json"); + + // レスポンスを送信 + exchange.sendResponseHeaders(200, resBody.getBytes(StandardCharsets.UTF_8).length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(resBody.getBytes(StandardCharsets.UTF_8)); + } + + // コマンドを生成して実行 + runCommand(signText); + } + + private static final Gson gson = new Gson(); + + public static String toCommand(String signText) { + int maxLength = 15; + List texts = new ArrayList<>(); + + for (int i = 0; i < signText.length(); i += maxLength) { + texts.add(signText.substring(i, Math.min(i + maxLength, signText.length()))); + } + + texts = texts.stream().map(text -> "'[\"" + text + "\"]'").collect(Collectors.toList()); + + List frontTexts = Stream.concat(texts.stream(), Stream.generate(() -> "'[\"\"]'")).limit(4).collect(Collectors.toList()); + if (texts.size() <= 4) { + return "give @p minecraft:oak_sign{BlockEntityTag:{front_text:{messages:[" + String.join(",", frontTexts) + "]}}}"; + } else { + List backTexts = Stream.concat(texts.stream(), Stream.generate(() -> "'[\"\"]'")).skip(4).limit(8).collect(Collectors.toList()); + return "give @p minecraft:oak_sign{BlockEntityTag:{front_text:{messages:[" + String.join(",", frontTexts) + "]},back_text:{messages:[" + String.join(",", backTexts) + "]}}}"; + } + } + + // 看板に書かれる文字から give コマンドを生成して実行する + private static void runCommand(String signText) { + ClientPlayNetworkHandler net = MinecraftClient.getInstance().getNetworkHandler(); + // null の場合は何もしない + if (net == null) return; + // コマンドを生成 + String command = toCommand(signText); + // コマンドを実行 + net.sendCommand(command); + } + + private record RequestJson( + String address, + float width, float height, + float x, float y, float z, + float rx, float ry, float rz + ) { + public String signText() { + return String.format( + "%s|%s|%s|%s|%s|%s|%s|%s|%s", + address, + floatFormat(x), floatFormat(y), floatFormat(z), + floatFormat(height), floatFormat(width), + floatFormat(rx), floatFormat(ry), floatFormat(rz) + ); + } + + private String floatFormat(float value) { + return Float.toString(value); + } + } + + private record ResponseJson(String signText) { + } +} diff --git a/src/main/java/marumasa/maruma_sign_webui/http/service/WebUIService.java b/src/main/java/marumasa/maruma_sign_webui/http/service/WebUIService.java new file mode 100644 index 0000000..9066bfe --- /dev/null +++ b/src/main/java/marumasa/maruma_sign_webui/http/service/WebUIService.java @@ -0,0 +1,71 @@ +package marumasa.maruma_sign_webui.http.service; + + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import marumasa.maruma_sign_webui.MarumaSignWebUI; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Objects; + +public class WebUIService { + + // アクセスできるファイルのパス一覧 + private static final String[] allowPaths = { + "/index.html", "/style.css", "/script.js", "/utils.js", "/favicon.ico" + }; + + public static void Handle(HttpExchange exchange) throws IOException { + + String path = exchange.getRequestURI().toString(); + + MarumaSignWebUI.LOGGER.info(String.format("Requested: %s", path)); + + // リクエストされたパスをリソースディレクトリ内のファイルの場所に変換 + String resource_path = toResourcePath(path); + + // レスポンスボディを構築 + String responseBody = Arrays.asList(allowPaths).contains(path) ? getResource(resource_path) : ""; + + // レスポンスヘッダ の設定 + Headers responseHeaders = exchange.getResponseHeaders(); + // Content-Type を設定 + String contentType = Files.probeContentType(Paths.get(resource_path)); + responseHeaders.set("Content-Type", contentType); + + // レスポンスを送信 + exchange.sendResponseHeaders(200, responseBody.getBytes(StandardCharsets.UTF_8).length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(responseBody.getBytes(StandardCharsets.UTF_8)); + } + } + + // リソースにあるファイルを読み込む + private static String getResource(String path) { + + // ファイルの内容が書き込まれる変数 + StringBuilder fileText = new StringBuilder(); + + try (InputStream is = WebUIService.class.getResourceAsStream(path); + BufferedReader br = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) { + String line; + while ((line = br.readLine()) != null) { + fileText.append(line); // ファイルの内容を1行ずつ読み込み + fileText.append("\n"); // 改行する + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + // ファイルの内容を return する + return fileText.toString(); + } + + private static String toResourcePath(String path) { + return "/webui" + path; + } +} \ No newline at end of file diff --git a/src/main/java/marumasa/maruma_sign_webui/util/PortManager.java b/src/main/java/marumasa/maruma_sign_webui/util/PortManager.java new file mode 100644 index 0000000..5cd2701 --- /dev/null +++ b/src/main/java/marumasa/maruma_sign_webui/util/PortManager.java @@ -0,0 +1,36 @@ +package marumasa.maruma_sign_webui.util; + +import java.net.InetSocketAddress; +import java.net.Socket; + +public class PortManager { + + public static int generate() { + // ポート番号をランダムに設定 + int port = random(); + // もしそのポート番号が既に使用されていたら + // 使用されていないポート番号にする + while (isPortInUse(port)) port = random(); + // ポート番号を返す + return port; + } + + // ポート番号が既に使用されているかどうか確認するメソッド + private static boolean isPortInUse(int port) { + try (Socket socket = new Socket()) { + socket.connect(new InetSocketAddress("localhost", port), 100); + return true; + } catch (Exception e) { + return false; + } + } + + private static int random() { + // ランダムな整数を生成 + int min = 2071; + int max = 2121; + + double randomDouble = Math.random(); // 0.0 以上 1.0 未満の実数 + return (int) (randomDouble * (max - min + 1)) + min; + } +} diff --git a/src/main/java/marumasa/maruma_sign_webui/util/Utils.java b/src/main/java/marumasa/maruma_sign_webui/util/Utils.java new file mode 100644 index 0000000..b92a9dd --- /dev/null +++ b/src/main/java/marumasa/maruma_sign_webui/util/Utils.java @@ -0,0 +1,21 @@ +package marumasa.maruma_sign_webui.util; + +import marumasa.maruma_sign_webui.MarumaSignWebUI; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.util.InputUtil; + +public class Utils { + // キーバインド 作成 + public static KeyBinding createKeyBinding(String name, int code) { + return new KeyBinding( + // ID作成 + "key." + MarumaSignWebUI.MOD_ID + "." + name, + + // どのキーか設定 + InputUtil.Type.KEYSYM, code, + + // カテゴリ設定 + "key.categories." + MarumaSignWebUI.MOD_ID + ); + } +} diff --git a/src/main/resources/assets/maruma_sign_webui/lang/en_us.json b/src/main/resources/assets/maruma_sign_webui/lang/en_us.json new file mode 100644 index 0000000..163f161 --- /dev/null +++ b/src/main/resources/assets/maruma_sign_webui/lang/en_us.json @@ -0,0 +1,4 @@ +{ + "text.maruma_sign_webui.open_menu": "Open web UI", + "key.categories.maruma_sign_webui": "MarumaSign WebUI" +} \ No newline at end of file diff --git a/src/main/resources/assets/maruma_sign_webui/lang/ja_jp.json b/src/main/resources/assets/maruma_sign_webui/lang/ja_jp.json new file mode 100644 index 0000000..853b6b7 --- /dev/null +++ b/src/main/resources/assets/maruma_sign_webui/lang/ja_jp.json @@ -0,0 +1,4 @@ +{ + "text.maruma_sign_webui.open_menu": "Web UI を開く", + "key.categories.maruma_sign_webui": "MarumaSign WebUI" +} \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..e958c87 --- /dev/null +++ b/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "maruma_sign_webui", + "version": "${version}", + "name": "MarumaSignWebUI", + "description": "", + "authors": [], + "contact": { + "repo": "https://github.com/USER/MarumaSignWebUI" + }, + "license": "MIT", + "icon": "assets/maruma_sign_webui/icon.png", + "environment": "client", + "entrypoints": { + "client": [ + "marumasa.maruma_sign_webui.client.MarumaSignWebUIClient" + ], + "main": [ + "marumasa.maruma_sign_webui.MarumaSignWebUI" + ] + }, + "depends": { + "fabricloader": ">=${loader_version}", + "fabric": "*", + "minecraft": "${minecraft_version}" + } +} diff --git a/src/main/resources/webui/index.html b/src/main/resources/webui/index.html new file mode 100644 index 0000000..c30f942 --- /dev/null +++ b/src/main/resources/webui/index.html @@ -0,0 +1,60 @@ + + + + + + + MarumaSign-WebUI + + + + + + +
+
+
+ + + + +
+
+ + + +
+
+ + + +
+
+ +
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/webui/script.js b/src/main/resources/webui/script.js new file mode 100644 index 0000000..8f5f07a --- /dev/null +++ b/src/main/resources/webui/script.js @@ -0,0 +1,50 @@ +// 入力した値を Minecraft の Mod に送信する +async function toMC() { + + // HTMLから 画像アドレス を取得 + let address = document.getElementById("address").value; + + // もし URL の長さが 30 より大きい場合 + if (address.length > 30) { + // 短縮URL を取得して address に代入する + address = await getShortURL(address); + } + + //---------- HTMLから値を取得 ---------- start + const width = document.getElementById("width").value; + const height = document.getElementById("height").value; + const x = document.getElementById("x").value; + const y = document.getElementById("y").value; + const z = document.getElementById("z").value; + const rx = document.getElementById("rx").value; + const ry = document.getElementById("ry").value; + const rz = document.getElementById("rz").value; + //---------- HTMLから値を取得 ---------- end + + const reqJson = { + "address": address, + "width": width, + "height": height, + "x": x, + "y": y, + "z": z, + "rx": rx, + "ry": ry, + "rz": rz + } + + const resJson = await postJson("./", reqJson); + + console.log(resJson); + +} + +// アップロードする画像ファイルが選択されたら +async function onFile(input) { + // 画像ファイルを取得 + const file = input.files[0]; + // アップロードして画像のURLを取得 + const url = await uploadFile(file); + // 画像アドレスのテキストボックスに画像のURLを入力 + document.getElementById("address").value = url; +} \ No newline at end of file diff --git a/src/main/resources/webui/style.css b/src/main/resources/webui/style.css new file mode 100644 index 0000000..3122519 --- /dev/null +++ b/src/main/resources/webui/style.css @@ -0,0 +1,161 @@ +body { + font-size: 100%; + margin: 0; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +label { + display: flex; +} + +a img { + height: 40px; +} + +.box { + width: 80%; + margin: 50px; + margin-left: auto; + margin-right: auto; +} + +.box p { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: block; + margin: 0; + +} + +.title { + text-align: center; + background-color: #002060; + padding: 0; + margin: 0; + font-size: 30px; + display: flex; + color: white; +} + +.title img { + max-width: 30%; + min-width: 300px; + max-height: 100px; + display: block; + margin-right: auto; +} + +.title p { + width: 20%; + max-height: 100px; + text-align: right; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.form { + font-size: 30px; + width: 40%; + margin: 5px; +} + +input { + font-size: 30px; + width: 60%; +} + +.box input { + font-size: 30px; + width: 60%; +} + +.run { + text-align: center; +} + +.command { + font-size: 30px; + width: 90%; + margin-top: 20px; + text-align: center; + overflow-wrap: normal; +} + +textarea { + display: block; + text-align: center; + width: 90%; + resize: none; + margin: auto; + margin-top: 20px; + font-size: 30px; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +.settings { + display: flex; +} + + +.toggle_input { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + z-index: 5; + opacity: 0; + cursor: pointer; +} + +.toggle_label { + width: 75px; + height: 35px; + background: #ccc; + position: relative; + display: inline-block; + border-radius: 40px; + transition: 0.4s; + box-sizing: border-box; +} + +.toggle_label:after { + content: ""; + position: absolute; + width: 35px; + height: 35px; + border-radius: 100%; + left: 0; + top: 0; + z-index: 2; + background: #fff; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.2); + transition: 0.4s; +} + +.toggle_input:checked+.toggle_label { + background-color: #4BD865; +} + +.toggle_input:checked+.toggle_label:after { + left: 40px; +} + +.toggle_button { + position: relative; + width: 75px; + height: 35px; + margin: auto; +} + +input#upload { + font: 1.1em sans-serif; + padding: 10px; +} + +.upload { + display: flex; + justify-content: flex-end; +} \ No newline at end of file diff --git a/src/main/resources/webui/utils.js b/src/main/resources/webui/utils.js new file mode 100644 index 0000000..f725972 --- /dev/null +++ b/src/main/resources/webui/utils.js @@ -0,0 +1,84 @@ +// Get リクエスト +// json を return する +function get(url) { + return new Promise((resolve) => { + fetch(url) + .then((res) => res.json()) + .then((json) => resolve(json)) + .catch(() => resolve(undefined)); + }); +} + +// Post リクエスト (formData) +// json を return する +function post(url, formData) { + return new Promise((resolve) => { + fetch(url, { + method: "POST", + body: formData + }) + .then((res) => res.json()) + .then((json) => resolve(json)) + .catch(() => resolve(undefined)); + }); +} +// Post リクエスト (Json) +// json を return する +function postJson(url, data) { + return new Promise((resolve) => { + fetch(url, { + method: "POST", + headers: {// JSON形式のデータのヘッダー + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + }) + .then((res) => res.json()) + .then((json) => resolve(json)) + .catch(() => resolve(undefined)); + }); +} + +async function getShortURL(url) { + let shorturl = localStorage.getItem(url); + + // ローカルストレージに短縮したURLがキャッシュされていたら + // それを return する + if (shorturl) return shorturl; + + // https://is.gd/ の URL短縮APIを利用して 短縮URLを生成 + json = await get( + `https://is.gd/create.php?format=json&url=${encodeURIComponent(url)}` + ); + // 送られてきた json に shorturl があるかどうか チェック + + // shorturl の値を置き換える + shorturl = json.shorturl; + + if (shorturl) { + //もし あったら + + // ローカルストレージを利用して 短縮したURLをキャッシュ + localStorage.setItem(url, shorturl); + + // ログを送信 + console.log(`URLを短縮しました: ${url} -> ${shorturl}`); + return shorturl; + } else { + //もし なかったら + //エラーのログを送信 + console.error("URLを短縮できませんでした"); + return url; + } +} + +// ファイルをアップロードする関数 +async function uploadFile(file) { + const url = "https://hm-nrm.h3z.jp/uploader/work.php"; // アップロード先のURL + const formData = new FormData(); + formData.append("files", file); + const json = await post(url, formData); + if (json == undefined) return "アップロードに失敗しました" + else if (json.files[0].error != undefined) return json.files[0].error; + return json.files[0].url; +} \ No newline at end of file