diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..e9f6b98 Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index f3bed41..bea1214 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,52 @@ -# Fabric Example Mod - Kotlin -![Gradle build](https://github.com/natanfudge/fabric-example-mod-kotlin/workflows/Gradle%20build/badge.svg) +# Git Mod - A Fabric Mod written in Kotlin + +A server-side git client usable within minecraft chat! + +![example](imgs/example.png) + +To use, navigate to the config folder and locate the `gitmod.json` configuration file. Here, you can define a gitPath (automatically the `datapacks` folder) and a dictionary of operators (name: UUID) for people who are allowed to utilize the git system. + +Then, you can type `/gitconfig reload` to reload the config. + +Now you can type `/git` and any sort of git command and it *should* just work! +Even aliases! + ## Setup -0. Create a new mod repository by pressing the "Use this template" button and clone the created repository. +0. Make sure you have Git installed are you system and available through your $PATH. If you are on Windows, it's very likely this mod will fail :(. + +1. Install this Mod along side fabric-language-kotlin + +2. Run the server / client to create an empty configuration file `config/gitmod.json` + +3. Edit the config to specify your git repo and the operator's UUID you would like to adjust + + { + "gitPath": "path to git repo (defaults to datapacks folder)", + "operators": { + "minecraft name": "minecraft UUID" + } + } + +4. ??? + +?. Success? + + +## Details + +At the moment, this is an incredibly cursed mod. Essentially, it takes the arguments of the `/git` cmd and passes it a shell process to run it in a coroutine. If a command is currently running (like a `git pull`), then no other invocations are allowed. This design is extremely restricted to avoid any weird bugs that might come up. + +Also, this mod will heavily rely on your personal git installation since it leverages your shell. In the future, the plan is to develop a client `/git` cmd and a `/gitraw` cmd where `/git` interfaces with the JGit library and `/gitraw` works thru the shell process. For now, we have jank :D. + +Do note that when working with services like `github`, you'll have to work with some sort of authentication to get your `git` to operate with `github` w/o you inputting a password into minecraft (since that will be blocked). This will involve some sort of ssh key or something, so do be aware. I suggest you gave the `git` installation on the server some sort of special email and username to differentiate from normal commits to your repo! + +## Why? -1. Import build.gradle file with IntelliJ IDEA +This mod is geared towards datapackers. When working on a server, it is not always possible to have an SFTP connection to access and work with the files. This means you might have to edit the files through a HTTPS panel which can be incredibly frustrating. SFTP also has a lue of issue and is not always reliable especially when trying to collaborate with others (like datapack jams). Since `git` is a popular tool and already used in the space, a tool like this can be incredibly helpful and potent. -2. Edit build.gradle and mod.json to suit your needs. - * The "mixins" object can be removed from mod.json if you do not need to use mixins. - * Please replace all occurences of "modid" with your own mod ID - sometimes, a different string may also suffice. -3. Run! +For example, when working on a datapack ran server, you can have a bunch of experiemental changes applied on a branch and then switch back to your main branch to avoid pollution. It's also extremely viable to have your entire local setup work seemlessly with the server since you can just spam `git commits` as a way of timestamping your changes and pushing to your server. ## License -This template is available under the CC0 license. Feel free to learn from it and incorporate it in your own projects. +This project is licensed under the MIT licenses. Any exceptions for this license are detailed in licensing in specific files. diff --git a/imgs/example.png b/imgs/example.png new file mode 100644 index 0000000..a8b64a4 Binary files /dev/null and b/imgs/example.png differ diff --git a/src/main/.DS_Store b/src/main/.DS_Store new file mode 100644 index 0000000..560dfe4 Binary files /dev/null and b/src/main/.DS_Store differ diff --git a/src/main/java/.DS_Store b/src/main/java/.DS_Store new file mode 100644 index 0000000..d33e768 Binary files /dev/null and b/src/main/java/.DS_Store differ diff --git a/src/main/java/net/.DS_Store b/src/main/java/net/.DS_Store new file mode 100644 index 0000000..0e1d0f5 Binary files /dev/null and b/src/main/java/net/.DS_Store differ diff --git a/src/main/java/net/rx/.DS_Store b/src/main/java/net/rx/.DS_Store new file mode 100644 index 0000000..7ca882d Binary files /dev/null and b/src/main/java/net/rx/.DS_Store differ diff --git a/src/main/java/net/rx/modules/ArgumentTokenizer.java b/src/main/java/net/rx/modules/ArgumentTokenizer.java new file mode 100644 index 0000000..e714619 --- /dev/null +++ b/src/main/java/net/rx/modules/ArgumentTokenizer.java @@ -0,0 +1,229 @@ +/*BEGIN_COPYRIGHT_BLOCK + * + * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software is Open Source Initiative approved Open Source Software. + * Open Source Initative Approved is a trademark of the Open Source Initiative. + * + * This file is part of DrJava. Download the current version of this project + * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/ + * + * END_COPYRIGHT_BLOCK*/ + +package net.rx.modules; + +import java.util.List; +import java.util.LinkedList; + +/** + * Utility class which can tokenize a String into a list of String arguments, + * with behavior similar to parsing command line arguments to a program. + * Quoted Strings are treated as single arguments, and escaped characters + * are translated so that the tokenized arguments have the same meaning. + * Since all methods are static, the class is declared abstract to prevent + * instantiation. + * @version $Id$ + */ +public abstract class ArgumentTokenizer { + private static final int NO_TOKEN_STATE = 0; + private static final int NORMAL_TOKEN_STATE = 1; + private static final int SINGLE_QUOTE_STATE = 2; + private static final int DOUBLE_QUOTE_STATE = 3; + + /** Tokenizes the given String into String tokens + * @param arguments A String containing one or more command-line style arguments to be tokenized. + * @return A list of parsed and properly escaped arguments. + */ + public static List tokenize(String arguments) { + return tokenize(arguments, false); + } + + /** Tokenizes the given String into String tokens. + * @param arguments A String containing one or more command-line style arguments to be tokenized. + * @param stringify whether or not to include escape special characters + * @return A list of parsed and properly escaped arguments. + */ + public static List tokenize(String arguments, boolean stringify) { + + LinkedList argList = new LinkedList(); + StringBuilder currArg = new StringBuilder(); + boolean escaped = false; + int state = NO_TOKEN_STATE; // start in the NO_TOKEN_STATE + int len = arguments.length(); + + // Loop over each character in the string + for (int i = 0; i < len; i++) { + char c = arguments.charAt(i); + if (escaped) { + // Escaped state: just append the next character to the current arg. + escaped = false; + currArg.append(c); + } + else { + switch(state) { + case SINGLE_QUOTE_STATE: + if (c == '\'') { + // Seen the close quote; continue this arg until whitespace is seen + state = NORMAL_TOKEN_STATE; + } + else { + currArg.append(c); + } + break; + case DOUBLE_QUOTE_STATE: + if (c == '"') { + // Seen the close quote; continue this arg until whitespace is seen + state = NORMAL_TOKEN_STATE; + } + else if (c == '\\') { + // Look ahead, and only escape quotes or backslashes + i++; + char next = arguments.charAt(i); + if (next == '"' || next == '\\') { + currArg.append(next); + } + else { + currArg.append(c); + currArg.append(next); + } + } + else { + currArg.append(c); + } + break; +// case NORMAL_TOKEN_STATE: +// if (Character.isWhitespace(c)) { +// // Whitespace ends the token; start a new one +// argList.add(currArg.toString()); +// currArg = new StringBuffer(); +// state = NO_TOKEN_STATE; +// } +// else if (c == '\\') { +// // Backslash in a normal token: escape the next character +// escaped = true; +// } +// else if (c == '\'') { +// state = SINGLE_QUOTE_STATE; +// } +// else if (c == '"') { +// state = DOUBLE_QUOTE_STATE; +// } +// else { +// currArg.append(c); +// } +// break; + case NO_TOKEN_STATE: + case NORMAL_TOKEN_STATE: + switch(c) { + case '\\': + escaped = true; + state = NORMAL_TOKEN_STATE; + break; + case '\'': + state = SINGLE_QUOTE_STATE; + break; + case '"': + state = DOUBLE_QUOTE_STATE; + break; + default: + if (!Character.isWhitespace(c)) { + currArg.append(c); + state = NORMAL_TOKEN_STATE; + } + else if (state == NORMAL_TOKEN_STATE) { + // Whitespace ends the token; start a new one + argList.add(currArg.toString()); + currArg = new StringBuilder(); + state = NO_TOKEN_STATE; + } + } + break; + default: + throw new IllegalStateException("ArgumentTokenizer state " + state + " is invalid!"); + } + } + } + + // If we're still escaped, put in the backslash + if (escaped) { + currArg.append('\\'); + argList.add(currArg.toString()); + } + // Close the last argument if we haven't yet + else if (state != NO_TOKEN_STATE) { + argList.add(currArg.toString()); + } + // Format each argument if we've been told to stringify them + if (stringify) { + for (int i = 0; i < argList.size(); i++) { + argList.set(i, "\"" + _escapeQuotesAndBackslashes(argList.get(i)) + "\""); + } + } + return argList; + } + + /** Inserts backslashes before any occurrences of a backslash or + * quote in the given string. Also converts any special characters + * appropriately. + */ + protected static String _escapeQuotesAndBackslashes(String s) { + final StringBuilder buf = new StringBuilder(s); + + // Walk backwards, looking for quotes or backslashes. + // If we see any, insert an extra backslash into the buffer at + // the same index. (By walking backwards, the index into the buffer + // will remain correct as we change the buffer.) + for (int i = s.length()-1; i >= 0; i--) { + char c = s.charAt(i); + if ((c == '\\') || (c == '"')) { + buf.insert(i, '\\'); + } + // Replace any special characters with escaped versions + else if (c == '\n') { + buf.deleteCharAt(i); + buf.insert(i, "\\n"); + } + else if (c == '\t') { + buf.deleteCharAt(i); + buf.insert(i, "\\t"); + } + else if (c == '\r') { + buf.deleteCharAt(i); + buf.insert(i, "\\r"); + } + else if (c == '\b') { + buf.deleteCharAt(i); + buf.insert(i, "\\b"); + } + else if (c == '\f') { + buf.deleteCharAt(i); + buf.insert(i, "\\f"); + } + } + return buf.toString(); + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/rx/modules/.DS_Store b/src/main/kotlin/net/rx/modules/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/src/main/kotlin/net/rx/modules/.DS_Store differ diff --git a/src/main/kotlin/net/rx/modules/GitCommand.kt b/src/main/kotlin/net/rx/modules/GitCommand.kt index 9b504e8..2df25f6 100644 --- a/src/main/kotlin/net/rx/modules/GitCommand.kt +++ b/src/main/kotlin/net/rx/modules/GitCommand.kt @@ -14,12 +14,13 @@ import net.minecraft.text.LiteralText import net.minecraft.text.Style import net.minecraft.text.Text import net.minecraft.util.Formatting +import net.rx.modules.ArgumentTokenizer.tokenize import java.io.IOException import java.util.concurrent.TimeUnit fun red(string: String): Text = LiteralText(string).setStyle(Style.EMPTY.withColor(Formatting.RED)) fun green(string: String): Text = LiteralText(string).setStyle(Style.EMPTY.withColor(Formatting.GREEN)) -fun dark_gray(string: String): Text = LiteralText(string).setStyle(Style.EMPTY.withColor(Formatting.DARK_GRAY)) +fun gray(string: String): Text = LiteralText(string).setStyle(Style.EMPTY.withColor(Formatting.GRAY)) class HomeCommand(private val dispatcher: CommandDispatcher) { @@ -55,7 +56,7 @@ class HomeCommand(private val dispatcher: CommandDispatcher): Int { GitConfig.reloadData() context.source.sendFeedback( - dark_gray("Successfully reloaded GitConfig"), true) + gray("Successfully reloaded GitConfig"), true) return 1 } @@ -73,10 +74,10 @@ class HomeCommand(private val dispatcher: CommandDispatcher