initial commit

This commit is contained in:
TopchetoEU 2024-02-10 12:12:02 +02:00
commit 67bca8c9e0
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
34 changed files with 1955 additions and 0 deletions

40
.github/workflows/build.yml vendored Executable file
View File

@ -0,0 +1,40 @@
# Automatically build the project and run any configured tests for every push
# and submitted pull request. This can help catch issues that only occur on
# certain platforms or Java versions, and provides a first line of defence
# against bad commits.
name: build
on: [pull_request, push]
jobs:
build:
strategy:
matrix:
# Use these Java versions
java: [
17, # Current Java LTS & minimum supported by Minecraft
]
# and run on both Linux and Windows
os: [ubuntu-22.04, windows-2022]
runs-on: ${{ matrix.os }}
steps:
- name: checkout repository
uses: actions/checkout@v3
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: setup jdk ${{ matrix.java }}
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java }}
distribution: 'microsoft'
- name: make gradle wrapper executable
if: ${{ runner.os != 'Windows' }}
run: chmod +x ./gradlew
- name: build
run: ./gradlew build
- name: capture build artifacts
if: ${{ runner.os == 'Linux' && matrix.java == '17' }} # Only upload artifacts built from latest java on one OS
uses: actions/upload-artifact@v3
with:
name: Artifacts
path: build/libs/

21
.gitignore vendored Executable file
View File

@ -0,0 +1,21 @@
*
!/src
!/src/**/*
!/doc
!/doc/**/*
!/.github
!/.github/**/*
!/.gitignore
!/.gitattributes
!/LICENSE
!/README.md
!/settings.gradle
!/build.gradle
!/gradle.properties
!/gradle
!/gradle/wrapper
!/gradle/wrapper/gradle-wrapper.properties

0
README.md Executable file
View File

46
build.gradle Executable file
View File

@ -0,0 +1,46 @@
plugins {
id 'fabric-loom' version '1.0-SNAPSHOT'
id 'maven-publish'
}
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
maven { url 'https://maven.terraformersmc.com/releases' }
}
dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
implementation 'com.github.topchetoeu:jscript:v0.8.7-beta'
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
modImplementation "com.terraformersmc:modmenu:${project.modmenu_version}"
// modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}"
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
toolchain.languageVersion = JavaLanguageVersion.of(17)
withSourcesJar()
}
sourceSets {
main.java.srcDirs = [ "src/java" ]
main.resources.srcDirs = [ "src/assets" ]
}
processResources {
filesMatching("fabric.mod.json") {
expand (
version: project.project_version,
name: project.project_name
)
}
}
base.archivesName = project.project_name
version = project.project_version
group = project.project_group

19
gradle.properties Executable file
View File

@ -0,0 +1,19 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx4G
org.gradle.parallel=true
# Dependencies
minecraft_version=1.19.4
yarn_mappings=1.19.4+build.2
loader_version=0.15.3
fabric_version=0.87.2+1.19.4
modmenu_version=6.3.1
# Project properties
project_version=0.0.1-alpha
project_name = mcscript
project_group me.topchetoeu
# Dependencies

View File

@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

12
settings.gradle Executable file
View File

@ -0,0 +1,12 @@
pluginManagement {
repositories {
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
mavenCentral()
gradlePluginPortal()
}
}
rootProject.name = properties.project_name

24
src/assets/fabric.mod.json Executable file
View File

@ -0,0 +1,24 @@
{
"schemaVersion": 1,
"id": "${name}",
"version": "${version}",
"name": "MC-Jscript",
"description": "A simple mod loader for Javascript mods.",
"authors": [ "TopcehtoEU" ],
"license": "MIT",
"icon": "${name}/icon.png",
"environment": "*",
"entrypoints": {
"client": [ "me.topchetoeu.mcscript.McScript" ],
"main": [ "me.topchetoeu.mcscript.McScript" ]
},
"mixins": [ "mcscript.mixins.json" ],
"depends": {
"fabricloader": ">=0.14.11",
"fabric-api": "*",
"minecraft": "~1.19.3",
"modmenu": "~1.19.3",
"java": ">=17"
},
"suggests": { }
}

15
src/assets/mcscript.mixins.json Executable file
View File

@ -0,0 +1,15 @@
{
"required": true,
"minVersion": "0.8",
"package": "me.topchetoeu.mcscript.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [],
"client": [
"ChatScreenMixin",
"MinecraftClientMixin",
"KeyboardMixin"
],
"injectors": {
"defaultRequire": 1
}
}

BIN
src/assets/mcscript/icon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

View File

@ -0,0 +1,24 @@
package me.topchetoeu.mcscript;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.text.Text;
@Environment(EnvType.CLIENT)
public class ClientCommandMessageReceiver implements MessageReceiver {
public final FabricClientCommandSource receiver;
@Override
public void sendError(String msg) {
receiver.sendError(Text.of(msg));
}
@Override
public void sendInfo(String msg) {
receiver.sendFeedback(Text.of(msg));
}
public ClientCommandMessageReceiver(FabricClientCommandSource receiver) {
this.receiver = receiver;
}
}

View File

@ -0,0 +1,21 @@
package me.topchetoeu.mcscript;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
@Environment(EnvType.CLIENT)
public class ClientMessageReceiver implements MessageReceiver {
public final MinecraftClient client = MinecraftClient.getInstance();
@Override
public void sendError(String msg) {
client.player.sendMessage(Text.literal("").append(msg).formatted(Formatting.RED));
}
@Override
public void sendInfo(String msg) {
client.player.sendMessage(Text.of(msg));
}
}

View File

@ -0,0 +1,12 @@
package me.topchetoeu.mcscript;
import java.util.Map;
import me.topchetoeu.jscript.core.engine.Context;
public interface EnvironmentFactory {
Iterable<String> dependancies();
String name();
void apply(Context ctx, Map<String, EnvironmentFactory> dependancies);
}

View File

@ -0,0 +1,141 @@
package me.topchetoeu.mcscript;
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.Engine;
import me.topchetoeu.jscript.core.engine.Environment;
import me.topchetoeu.jscript.core.engine.values.Values;
import me.topchetoeu.jscript.lib.Internals;
import me.topchetoeu.jscript.utils.filesystem.File;
import me.topchetoeu.jscript.utils.filesystem.Filesystem;
import me.topchetoeu.jscript.utils.filesystem.LineReader;
import me.topchetoeu.jscript.utils.filesystem.LineWriter;
import me.topchetoeu.jscript.utils.filesystem.RootFilesystem;
import me.topchetoeu.jscript.utils.filesystem.STDFilesystem;
import me.topchetoeu.jscript.utils.permissions.PermissionsManager;
import me.topchetoeu.jscript.utils.permissions.PermissionsProvider;
import me.topchetoeu.mcscript.events.ChatMessageCallback;
import me.topchetoeu.mcscript.gui.DevScreen;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.minecraft.client.MinecraftClient;
import net.minecraft.server.command.CommandManager;
import net.minecraft.text.Text;
public class McScript implements ModInitializer, ClientModInitializer {
private boolean openDev = false;
public static final Logger LOGGER = LoggerFactory.getLogger("jscript");
private void execute(int i, Environment env, Engine engine, String raw, MessageReceiver msg) {
try {
var res = engine.pushMsg(false, env, new Filename("repl", i + ".js"), raw, null).await();
msg.sendInfo(Values.toReadable(new Context(engine, env), res));
}
catch (RuntimeException e) {
msg.sendError(Values.errorToReadable(e, ""));
}
}
private Environment createEnv(String location, LineReader in, LineWriter out) {
var env = new Environment();
var inFile = File.ofLineReader(in);
var outFile = File.ofLineWriter(out);
Internals.apply(env);
var fs = new RootFilesystem(PermissionsProvider.get(env));
fs.protocols.put("std", new STDFilesystem().add("in", inFile).add("out", outFile));
var perms = new PermissionsManager();
env.add(PermissionsProvider.ENV_KEY, perms);
env.add(Filesystem.ENV_KEY, fs);
env.global.define(null, "env", true, location);
return env;
}
@Override public void onInitialize() {
var engine = new Engine();
ServerLifecycleEvents.SERVER_STARTING.register(server -> {
engine.start();
var env = createEnv("SERVER", () -> null, value -> {
for (var line : value.split("\n", -1)) LOGGER.info(line);
});
var i = new int[1];
server.getCommandManager().getDispatcher().register(CommandManager.literal("msc")
.then(CommandManager.argument("code", greedyString()).executes(c -> {
String str = getString(c, "code");
var receiver = new ServerCommandMessageReceiver(c.getSource());
execute(i[0]++, env, engine, str, receiver);
return 1;
}))
);
});
ServerLifecycleEvents.SERVER_STOPPED.register(server -> {
engine.stop();
});
}
@net.fabricmc.api.Environment(EnvType.CLIENT)
@Override public void onInitializeClient() {
var dev = new DevScreen();
var engine = new Engine();
ClientLifecycleEvents.CLIENT_STARTED.register((client) -> {
try {
Files.createDirectories(Path.of("scripts"));
}
catch (IOException e) { /* so be it */ }
var receiver = new ClientMessageReceiver();
var env = createEnv("CLIENT", () -> null, value -> MinecraftClient.getInstance().player.sendMessage(Text.of(value)));
engine.start();
var i = new int[1];
ChatMessageCallback.EVENT.register(args -> {
if (args.message.startsWith("#")) {
execute(i[0]++, env, engine, args.message.substring(1), receiver);
args.cancelled = true;
}
});
ClientTickEvents.END_CLIENT_TICK.register((_1) -> {
if (openDev) {
client.setScreenAndRender(dev);
openDev = false;
}
});
ClientCommandRegistrationCallback.EVENT.register((disp, _1) -> {
disp.register(ClientCommandManager.literal("dev")
.executes(c -> {
openDev = true;
return 1;
})
);
});
});
ClientLifecycleEvents.CLIENT_STOPPING.register((client) -> {
engine.stop();
});
}
}

View File

@ -0,0 +1,6 @@
package me.topchetoeu.mcscript;
public interface MessageReceiver {
public void sendError(String msg);
public void sendInfo(String msg);
}

View File

@ -0,0 +1,21 @@
package me.topchetoeu.mcscript;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
public class ServerCommandMessageReceiver implements MessageReceiver {
public final ServerCommandSource receiver;
@Override
public void sendInfo(String msg) {
receiver.sendFeedback(Text.of(msg), false);
}
@Override
public void sendError(String msg) {
receiver.sendError(Text.of(msg));
}
public ServerCommandMessageReceiver(ServerCommandSource receiver) {
this.receiver = receiver;
}
}

View File

@ -0,0 +1,17 @@
package me.topchetoeu.mcscript.events;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
public interface ChatMessageCallback {
public static class ChatArgs {
public String message;
public boolean cancelled;
}
void execute(ChatArgs args);
public static final Event<ChatMessageCallback> EVENT = EventFactory.createArrayBacked(ChatMessageCallback.class, arr -> args -> {
for (var el : arr) el.execute(args);
});
}

View File

@ -0,0 +1,40 @@
package me.topchetoeu.mcscript.gui;
import net.minecraft.client.util.math.MatrixStack;
public class ButtonWidget extends Widget {
public interface ClickHandler {
void click();
}
public final ClickHandler handler;
public final String text;
@Override
public void render(MatrixStack mat, int mx, int my, float delta) {
if (isMouseOver(mx, my)) fill(mat, x, y, x + w, y + h, theme.get("bg-2"));
else fill(mat, x, y, x + w, y + h, theme.get("bg-1"));
theme.font.draw(mat, text, x + (w - theme.font.getWidth(text)) / 2, y + (h - theme.fontSize()) / 2, theme.get("text"));
super.render(mat, mx, my, delta);
}
@Override
public boolean mouseClicked(double mx, double my, int button) {
if (isMouseOver(mx, my) && button == 0) {
handler.click();
return true;
}
return false;
}
@Override
protected boolean hasBorders() { return true; }
public ButtonWidget(Theme theme, String text, ClickHandler handler) {
super(theme);
this.handler = handler;
this.text = text;
}
}

View File

@ -0,0 +1,82 @@
package me.topchetoeu.mcscript.gui;
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
import net.minecraft.client.util.math.MatrixStack;
public class DevEditorWidget extends Widget {
private DevTextBoxWidget tab;
private void updateTab() {
if (tab == null) return;
tab.setMultiline(true).setRect(x, y + theme.fontSize() + theme.get("padding-s"), w, h - theme.fontSize() - theme.get("padding-s"));
tab.theme.setTheme(theme);
}
@Override
protected boolean hasBorders() { return false; }
@Override
public Widget setRect(int x, int y, int w, int h) {
super.setRect(x, y, w, h);
updateTab();
return this;
}
public DevTextBoxWidget get() {
return tab;
}
public void set(DevTextBoxWidget tab) {
if (this.tab == tab) return;
this.tab = tab;
updateTab();
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
if (tab == null) return false;
return tab.keyPressed(keyCode, scanCode, modifiers);
}
@Override
public boolean charTyped(char chr, int modifiers) {
if (tab == null) return false;
return tab.charTyped(chr, modifiers);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (tab == null) return false;
return tab.mouseClicked(mouseX, mouseY, button);
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
if (tab == null) return false;
return tab.mouseScrolled(mouseX, mouseY, amount);
}
@Override
public void appendNarrations(NarrationMessageBuilder nmb) {
}
@Override
public SelectionType getType() {
return SelectionType.NONE;
}
@Override
public void render(MatrixStack mat, int mx, int my, float delta) {
if (tab == null) {
String text = "No file opened :(";
int cx = w - theme.font.getWidth(text);
int cy = h - theme.fontSize();
fill(mat, x, y, x + w, y + h, theme.get("bg-1"));
theme.font.draw(mat, text, x + cx / 2, x + cy / 2, theme.get("text"));
}
else {
tab.render(mat, mx, my, delta);
fill(mat, x, y, x + w, y + theme.fontSize() + 2, theme.get("bg-2"));
theme.font.draw(mat, tab.getFile().getAbsoluteFile().toString(), x + 5, y + 2, theme.get("text"));
}
}
public DevEditorWidget(Theme theme) {
super(theme);
}
}

View File

@ -0,0 +1,165 @@
package me.topchetoeu.mcscript.gui;
import java.io.File;
import java.io.IOException;
import org.lwjgl.glfw.GLFW;
import me.topchetoeu.mcscript.gui.OpenFileScreen.OpenHandler;
import me.topchetoeu.mcscript.gui.SaveFileScreen.SaveHandler;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
public class DevScreen extends Screen implements OpenHandler, SaveHandler {
private enum Focus {
EDITOR,
MENU,
}
private Theme theme;
private DevEditorWidget editor;
private DevTabListWidget tabs;
private Focus focus = Focus.EDITOR;
private int untitledI = 1;
// private int altI = 0;
// private void drawFileMenu(MatrixStack matrices) {
// int h = theme.fontSize() + 2;
// int x = 1;
// int y = height - 15;
// int i = 0;
// fill(matrices, 0, this.height - h, this.width, this.height, Argb.getArgb(255, 40, 40, 40));
// for (var el : ListExt.of("Save", "Open", "Close", "Quit")) {
// int w = font.getWidth(el) + 10;
// if (focus == Focus.MENU && altI == i++) {
// fill(matrices, x, y, x + w, y + h, Argb.getArgb(255, 80, 80, 80));
// }
// font.draw(matrices, el, x + 5, y + 1, Argb.getArgb(255, 255, 255, 255));
// x += w;
// }
// }
public void fileOpened(File file) {
try {
tabs.add(new DevTextBoxWidget(file, theme));
}
catch (IOException e) {
e.printStackTrace();
}
}
public void fileSaved(File file) {
var tab = tabs.getTab();
if (tab != null) {
try {
tab.saveFileAs(file);
}
catch (IOException e) {
e.printStackTrace();
}
}
}
private int getUntitled() {
while (true) {
int i = untitledI++;
if (!new File("untitled" + i + ".ms").exists()) return i;
}
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
try {
boolean ctrl = (modifiers & GLFW.GLFW_MOD_CONTROL) != 0;
boolean alt = (modifiers & GLFW.GLFW_MOD_ALT) != 0;
boolean shift = (modifiers & GLFW.GLFW_MOD_SHIFT) != 0;
if (ctrl) {
if (keyCode == GLFW.GLFW_KEY_N) {
tabs.add(new DevTextBoxWidget(new File("untitled" + getUntitled() + ".ms"), theme));
return true;
}
if (keyCode == GLFW.GLFW_KEY_W) {
tabs.close();
return true;
}
if (keyCode == GLFW.GLFW_KEY_S) {
if (!alt) tabs.getTab().saveFile();
else client.setScreen(new SaveFileScreen(this, theme, new File(""), Text.of("Save file:"), this));
return true;
}
if (keyCode == GLFW.GLFW_KEY_O) {
client.setScreen(new OpenFileScreen(this, theme, new File(""), Text.of("Open file:"), this));
return true;
}
if (keyCode == GLFW.GLFW_KEY_TAB) {
if (shift) tabs.prev();
else tabs.next();
return true;
}
}
else {
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
client.setScreen(null);
return true;
}
if (keyCode == GLFW.GLFW_KEY_LEFT_ALT) {
if (focus == Focus.MENU) focus = Focus.EDITOR;
else focus = Focus.MENU;
}
}
}
catch (Throwable e) {
e.printStackTrace();
}
return this.getFocused() != null && this.getFocused().keyPressed(keyCode, scanCode, modifiers);
}
@SuppressWarnings("all")
protected void init() {
if (theme == null) {
theme = Theme.dark(MinecraftClient.getInstance().textRenderer);
editor = new DevEditorWidget(theme);
tabs = new DevTabListWidget(theme, tab -> editor.set(tab));
}
addDrawableChild(editor);
addDrawableChild(tabs);
setFocused(editor);
editor.setRect(0, 0, width - 100, height);
tabs.setRect(width - 100, 0, 100, height);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
var res = super.mouseClicked(mouseX, mouseY, button);
return res;
}
// @Override
// public boolean changeFocus(boolean lookForwards) {
// return true;
// }
public DevScreen() {
super(Text.of("McScript IDE"));
// try {
// this.tabs.add(new DevTab(new File("test1.ms"), font));
// this.tabs.add(new DevTab(new File("test1.ms"), font));
// this.tabs.add(new DevTab(new File("test3.ms"), font));
// }
// catch (IOException e) {
// e.printStackTrace();
// }
// this.tabs.add(new DevTab(0, "", "test2.ms"));
// this.tabs.add(new DevTab(0, "", "test3.ms"));
}
}

View File

@ -0,0 +1,121 @@
package me.topchetoeu.mcscript.gui;
import java.util.ArrayList;
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
import net.minecraft.client.util.math.MatrixStack;
public class DevTabListWidget extends Widget {
public static interface TabChangeHandle {
void tabChanged(DevTextBoxWidget tab);
}
@Override
protected boolean hasBorders() { return false; }
public final TabChangeHandle changeHandle;
private int tabI = -1;
private ArrayList<DevTextBoxWidget> tabs = new ArrayList<>();
public DevTextBoxWidget getTab() {
if (tabs.size() == 0) return null;
return tabs.get(tabI);
}
public void setTab(DevTextBoxWidget tab) {
if (tabs.contains(tab)) tabs.add(tab);
tabI = tabs.indexOf(tab);
changeHandle.tabChanged(getTab());
}
public void next() {
if (tabs.size() < 2) return;
tabI++;
if (tabI >= tabs.size()) tabI = 0;
changeHandle.tabChanged(getTab());
}
public void prev() {
if (tabs.size() < 2) return;
tabI--;
if (tabI < 0) tabI = tabs.size() - 1;
changeHandle.tabChanged(getTab());
}
public void add(DevTextBoxWidget tab) {
tabs.add(++tabI, tab);
changeHandle.tabChanged(getTab());
}
public void close() {
if (tabs.size() == 0) return;
tabs.remove(tabI);
if (tabI == tabs.size()) {
tabI--;
changeHandle.tabChanged(getTab());
}
}
@Override
public boolean mouseClicked(double mx, double my, int button) {
if (!isMouseOver(mx, my)) return false;
mx -= x; my -= y;
int i = (int)((my - theme.get("padding-l")) / (theme.fontSize() + 4));
if (i >= 0 && i < tabs.size()) {
if (button == 2) {
tabs.remove(tabI);
if (tabI == tabs.size()) {
tabI--;
changeHandle.tabChanged(getTab());
}
}
else {
tabI = i;
changeHandle.tabChanged(getTab());
}
}
return true;
}
@Override
public void render(MatrixStack mat, int mx, int my, float delta) {
int h = theme.fontSize() + 4;
mat.push();
mat.translate(x, y, 0);
fill(mat, 0, 0, w, theme.get("padding-l"), theme.get("bg-3"));
mat.push();
mat.translate(0, theme.get("padding-l"), 0);
for (int i = 0; i < tabs.size(); i++) {
int col = theme.get("bg-3");
if (tabI == i) col = theme.get("bg-4");
fill(mat, 0, i * h, w, (i + 1) * h, col);
var name = tabs.get(i).getFile().getName();
if (!tabs.get(i).fileIsSaved()) name = "*" + name;
theme.font.draw(mat, name, 5, i * h + 2, theme.get("text"));
}
mat.pop();
fill(mat, 0, tabs.size() * h + theme.get("padding-l"), w, this.h, theme.get("bg-3"));
mat.pop();
}
@Override
public void appendNarrations(NarrationMessageBuilder var1) {
}
@Override
public SelectionType getType() {
return SelectionType.NONE;
}
public DevTabListWidget(Theme theme, TabChangeHandle changeHandle) {
super(theme);
this.changeHandle = changeHandle;
}
}

View File

@ -0,0 +1,95 @@
package me.topchetoeu.mcscript.gui;
import java.nio.file.Files;
import net.minecraft.client.util.math.MatrixStack;
import java.io.File;
import java.io.IOException;
public class DevTextBoxWidget extends TextBoxWidget {
private File file;
private FileTracker tracker;
private boolean saved;
private String conflict = null;
public File getFile() {
return file;
}
public boolean fileExists() {
return tracker.exists();
}
public boolean fileIsSaved() {
return saved;
}
public boolean fileHasConflict() {
return conflict != null;
}
public String getFileConflictReason() {
return conflict;
}
public void saveFile() throws IOException {
Files.writeString(file.toPath(), getValue());
tracker.wasChanged();
saved = true;
conflict = null;
}
public void saveFileAs(File file) throws IOException {
file.getParentFile().mkdirs();
Files.writeString(file.toPath(), getValue());
tracker.wasChanged();
this.file = file;
this.tracker = new FileTracker(file);
saved = true;
conflict = null;
}
@Override
protected boolean hasBorders() { return false; }
@Override
protected void textChanged() {
saved = false;
}
public void update() {
if (tracker.wasCreated()) {
conflict = "File was created";
saved = false;
}
try {
if (tracker.wasChanged()) {
if (saved) this.setValue(Files.readString(file.toPath()));
else conflict = "File was changed";
}
}
catch (IOException e) {
e.printStackTrace();
}
if (tracker.wasDeleted()) {
saved = false;
conflict = null;
}
}
@Override
public void render(MatrixStack mat, int x, int y, float delta) {
update();
super.render(mat, x, y, delta);
}
public DevTextBoxWidget(File file, Theme theme) throws IOException {
super(theme);
this.file = file;
this.tracker = new FileTracker(file);
if (tracker.exists()) {
saved = true;
this.setMultiline(true);
this.setValue(Files.readString(file.toPath()));
}
else saved = false;
}
}

View File

@ -0,0 +1,94 @@
package me.topchetoeu.mcscript.gui;
import java.io.File;
import org.lwjgl.glfw.GLFW;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
public abstract class FileDialogScreen extends Screen {
public final Screen parent;
public final Theme theme;
protected final FileListWidget list;
protected final ButtonWidget ok;
protected final ButtonWidget cancel;
protected final TextBoxWidget name;
protected abstract String okButtonText();
protected abstract void okPressed(File file);
protected abstract void selected(File file);
private void submit() {
okPressed(new File(list.cd(), name.getValue()).toPath().toAbsolutePath().toFile());
}
private int headerHeight() {
return theme.get("padding-m") * 2 + theme.fontSize();
}
private int footerHeight() {
return (theme.get("padding-m") + theme.get("padding-s")) * 2 + theme.fontSize();
}
@Override
protected void init() {
int padl = theme.get("padding-l");
int padm = theme.get("padding-m");
int pads = theme.get("padding-s");
int btnw = 50;
int btnh = pads * 2 + theme.fontSize();
int btny = height - padm - btnh;
int inputw = width - 100 - padl * 4;
list.setRect(padl, headerHeight(), width - padl * 2, height - headerHeight() - footerHeight());
ok.setRect(padl, btny, btnw, btnh);
cancel.setRect(padl + 50 + padl, btny, btnw, btnh);
name.setPos(padl + 100 + padl * 2, btny).setWidth(inputw);
addDrawableChild(list);
addDrawableChild(ok);
addDrawableChild(cancel);
addDrawableChild(name);
}
@Override
public void render(MatrixStack mat, int mx, int my, float delta) {
fill(mat, 0, 0, width, height, theme.get("bg-1"));
list.render(mat, mx, my, delta);
fill(mat, list.x, 0, list.w + list.x, list.y - 1, theme.get("bg-1"));
fill(mat, list.x, list.y + list.h + 1, list.w + list.x, height, theme.get("bg-1"));
theme.font.draw(mat, title, theme.get("padding-l"), theme.get("padding-m"), theme.get("text"));
ok.render(mat, mx, my, delta);
cancel.render(mat, mx, my, delta);
name.render(mat, mx, my, delta);
}
@Override
public void close() {
client.setScreen(parent);
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
close();
return true;
}
if (keyCode == GLFW.GLFW_KEY_ENTER) {
submit();
return true;
}
return super.keyPressed(keyCode, scanCode, modifiers);
}
protected FileDialogScreen(Screen parent, Theme theme, File root, Text title) {
super(title);
this.parent = parent;
this.theme = theme;
list = new FileListWidget(theme, root, this::selected);
name = new TextBoxWidget(theme);
ok = new ButtonWidget(theme, okButtonText(), this::submit);
cancel = new ButtonWidget(theme, "Cancel", this::close);
}
}

View File

@ -0,0 +1,146 @@
package me.topchetoeu.mcscript.gui;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.util.math.MatrixStack;
public class FileListWidget extends Widget {
public interface SelectHandler {
void selected(File f);
}
public final SelectHandler handler;
private File res = null;
private File currFile;
private List<String> entries = new ArrayList<>();
private float scroll = 0;
private void setEntries() {
if (currFile == null) {
entries.clear();
for (var root : File.listRoots()) {
entries.add(root.toString());
}
}
else if (currFile.isFile() || currFile.listFiles() == null) {
res = currFile;
handler.selected(currFile);
currFile = currFile.getParentFile();
return;
}
else {
this.entries.clear();
this.entries.add("(drives)");
this.entries.add("..");
for (var el : currFile.listFiles()) {
if (el.isDirectory()) this.entries.add(el.getName() + "/");
}
for (var el : currFile.listFiles()) {
if (el.isFile()) this.entries.add(el.getName());
}
}
updateScroll();
}
private void updateScroll() {
float _h = entries.size() * (theme.fontSize() + 4) - h;
if (scroll > _h) scroll = _h;
if (scroll < 0) scroll = 0;
}
private int entryHeight() {
return theme.fontSize() + 4;
}
@Override
protected boolean hasBorders() { return true; }
public File result() {
return res.toPath().toAbsolutePath().toFile();
}
public File cd() {
return currFile.toPath().toAbsolutePath().toFile();
}
@Override
public void render(MatrixStack mat, int mx, int my, float delta) {
renderSetup();
mat.push();
mat.translate(x, y, 0);
mx -= x; my -= y;
fill(mat, 0, 0, w, h, theme.get("bg-1"));
mat.push();
mat.translate(0, -scroll, 0);
my += scroll;
for (int i = 0; i < entries.size(); i++) {
int x1 = 0, y1 = i * entryHeight(), x2 = w, y2 = (i + 1) * entryHeight();
if (mx >= x1 && mx < x2 && my >= y1 && my < y2) {
fill(mat, x1, y1, x2, y2, theme.get("bg-3"));
}
else if (i % 2 == 0) {
fill(mat, x1, y1, x2, y2, theme.get("bg-2"));
}
theme.font.draw(mat, entries.get(i), theme.get("padding-l"), y1 + 3, theme.get("text"));
}
mat.pop();
mat.pop();
renderFinalize();
super.render(mat, mx, my, delta);
}
@Override
public boolean mouseClicked(double mx, double my, int button) {
if (button != 0 || !isMouseOver(mx, my)) return false;
mx -= x; my -= y - scroll;
int i = (int)(my / entryHeight());
if (i < 0 || i >= entries.size()) return false;
if (currFile == null) {
currFile = new File(entries.get(i));
}
else {
if (i == 0) currFile = null;
else if (i == 1) currFile = currFile.getParentFile();
else currFile = new File(currFile, entries.get(i));
}
setEntries();
return true;
}
@Override
public boolean mouseScrolled(double mx, double my, double amount) {
if (isMouseOver(mx, my)) {
scroll -= amount * 10;
updateScroll();
return true;
}
return false;
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
return false;
}
public FileListWidget(Theme theme, File root, SelectHandler handler) {
super(theme);
this.handler = handler;
this.currFile = root.toPath().toAbsolutePath().toFile();
if (this.currFile.isFile()) throw new RuntimeException("wtf");
setEntries();
}
}

View File

@ -0,0 +1,65 @@
package me.topchetoeu.mcscript.gui;
import java.io.File;
public class FileTracker {
public final File file;
private long lastChange = 0;
private boolean exists = false;
private boolean created = false;
private boolean deleted = false;
private boolean changed = false;
public void update() {
if (file.exists()) {
if (!exists) created = changed = true;
exists = true;
if (lastChange < file.lastModified()) {
changed = true;
lastChange = file.lastModified();
}
}
else {
if (exists) changed = deleted = true;
exists = created = false;
lastChange = 0;
}
}
public boolean exists() {
update();
return exists;
}
public boolean wasChanged() {
update();
if (changed) {
changed = false;
return true;
}
else return false;
}
public boolean wasCreated() {
update();
if (created) {
created = false;
return true;
}
else return false;
}
public boolean wasDeleted() {
update();
if (deleted) {
deleted = false;
return true;
}
else return false;
}
public FileTracker(File file) {
this.file = file;
if (exists = file.exists()) lastChange = file.lastModified();
else lastChange = 0;
}
}

View File

@ -0,0 +1,33 @@
package me.topchetoeu.mcscript.gui;
import java.io.File;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
public class OpenFileScreen extends FileDialogScreen {
public interface OpenHandler {
void fileOpened(File file);
}
public final OpenHandler handler;
@Override
protected String okButtonText() { return "Open"; }
@Override
protected void okPressed(File file) {
handler.fileOpened(file);
close();
}
@Override
protected void selected(File file) {
handler.fileOpened(file);
close();
}
protected OpenFileScreen(Screen parent, Theme theme, File root, Text title, OpenHandler handler) {
super(parent, theme, root, title);
this.handler = handler;
}
}

View File

@ -0,0 +1,33 @@
package me.topchetoeu.mcscript.gui;
import java.io.File;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
public class SaveFileScreen extends FileDialogScreen {
public interface SaveHandler {
void fileSaved(File file);
}
public final SaveHandler handler;
@Override
protected String okButtonText() { return "Save"; }
@Override
protected void okPressed(File file) {
handler.fileSaved(file);
close();
}
@Override
protected void selected(File file) {
handler.fileSaved(file);
close();
}
protected SaveFileScreen(Screen parent, Theme theme, File root, Text title, SaveHandler handler) {
super(parent, theme, root, title);
this.handler = handler;
}
}

View File

@ -0,0 +1,425 @@
package me.topchetoeu.mcscript.gui;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.glfw.GLFW;
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
import net.minecraft.client.gui.screen.narration.NarrationPart;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.ColorHelper.Argb;
public class TextBoxWidget extends Widget {
private boolean multiline = false;
private ArrayList<ArrayList<Character>> lines = new ArrayList<>();
private String value;
private int xPos = 0, yPos = 0;
private float xScroll = 0, yScroll = 0;
private int maxLineLen = 0;
private int fontWidth() {
return 6;
}
private int fontHeight() {
return theme.fontSize();
}
private void updateValue() {
var res = new StringBuilder();
for (int i = 0; i < lines.size(); i++) {
if (i != 0) res.append('\n');
for (var c : lines.get(i)) {
res.append(c);
}
if (maxLineLen < lines.get(i).size()) maxLineLen = lines.get(i).size();
}
value = res.toString();
}
private void updatePos() {
int realX = xPos;
if (yPos >= lines.size()) yPos = lines.size() - 1;
if (yPos < 0) yPos = 0;
if (realX > lines.get(yPos).size()) realX = lines.get(yPos).size();
if (xPos < 0) xPos = 0;
xScroll = Math.min(xScroll, (maxLineLen) * fontWidth() - fontWidth());
yScroll = Math.min(yScroll, (lines.size()) * fontHeight() - fontHeight());
if (xScroll < 0) xScroll = 0;
if (yScroll < 0) yScroll = 0;
}
private void updateScroll() {
updatePos();
int realX = getCursorX();
yScroll = Math.min(yScroll, yPos * fontHeight());
yScroll = Math.max(yScroll, (yPos + 1) * fontHeight() - h + theme.get("padding-s") * 2);
xScroll = Math.min(xScroll, realX * fontWidth());
xScroll = Math.max(xScroll, realX * fontWidth() - w + theme.get("padding-s") * 2);
}
private List<Character> getLine() {
updatePos();
return lines.get(yPos);
}
public String getValue() { return value; }
public TextBoxWidget setValue(String val) {
lines.clear();
for (var line : val.split("\n")) {
var chars = new ArrayList<Character>();
for (var c : line.toCharArray()) {
if (c == '\r') continue;
chars.add(c);
}
lines.add(chars);
}
value = val;
updatePos();
textChanged();
return this;
}
public int getCursorX() {
updatePos();
if (xPos > getLine().size()) return getLine().size();
else return xPos;
}
public void setCursorX(int val) {
xPos = val;
if (xPos < 0) xPos = 0;
if (xPos > getLine().size()) xPos = getLine().size();
updateScroll();
}
public void changeCursorX(int delta) {
setCursorX(getCursorX() + delta);
}
public int getCursorY() {
updatePos();
return yPos;
}
public void setCursorY(int val) {
yPos = val;
if (yPos < 0) yPos = 0;
if (yPos >= lines.size()) yPos = lines.size() - 1;
updateScroll();
}
public void changeCursorY(int delta) {
setCursorY(yPos + delta);
}
public float getScrollX() {
updatePos();
return xScroll;
}
public void setScrollX(float val) {
xScroll = val;
updatePos();
}
public void changeScrollX(float delta) {
setScrollX(getScrollX() + delta);
}
public float getScrollY() {
updatePos();
return yScroll;
}
public void setScrollY(float val) {
yScroll = val;
updatePos();
}
public void changeScrollY(float delta) {
setScrollY(getScrollY() + delta);
}
public boolean isMultiline() { return multiline; }
public TextBoxWidget setMultiline(boolean value) {
if (value == multiline) return this;
if (value) {
setHeight(theme.get("padding-s") * 2 + theme.fontSize());
var first = lines.get(0);
lines.clear();
lines.add(first);
textChanged();
}
multiline = value;
return this;
}
@Override
protected boolean hasBorders() { return true; }
public void input(char c) {
updatePos();
xPos = getCursorX();
getLine().add(xPos, c);
updateValue();
xPos++;
updateScroll();
textChanged();
}
public void newLine() {
if (!multiline) return;
updatePos();
var newLine = new ArrayList<Character>();
var currLine = getLine();
while (currLine.size() > xPos) {
newLine.add(currLine.remove(xPos));
}
lines.add(yPos + 1, newLine);
changeCursorY(1);
setCursorX(0);
updateScroll();
updateValue();
textChanged();
}
public void backspace() {
updatePos();
if (xPos == 0) {
if (yPos == 0) return;
var line = lines.remove(yPos);
changeCursorY(-1);
setCursorX(getLine().size());
getLine().addAll(line);
}
else {
changeCursorX(-1);
getLine().remove(xPos);
}
updateScroll();
updateValue();
textChanged();
}
public void delete() {
updatePos();
if (xPos == getLine().size()) {
if (yPos == lines.size() - 1) return;
getLine().addAll(lines.remove(yPos + 1));
}
else {
getLine().remove(xPos);
}
updateScroll();
updateValue();
textChanged();
}
public void home() {
setCursorX(0);
updateScroll();
}
public void end() {
setCursorX(getLine().size());
updateScroll();
}
public void tab() {
for (int i = getCursorX() % 4; i < 4; i++) {
input(' ');
}
}
public void right(boolean words) {
if (words) {
var line = getLine();
if (getCursorX() >= getLine().size()) {
if (getCursorY() == lines.size() - 1) return;
changeCursorY(1);
setCursorX(0);
}
else {
if (line.get(getCursorX()) == ' ') {
for (; getCursorX() < getLine().size(); changeCursorX(1)) {
if (line.get(getCursorX()) != ' ') break;
}
}
else {
for (; getCursorX() < getLine().size(); changeCursorX(1)) {
if (line.get(getCursorX()) == ' ') break;
}
for (; getCursorX() < getLine().size(); changeCursorX(1)) {
if (line.get(getCursorX()) != ' ') break;
}
}
}
}
else if (getCursorX() >= getLine().size()) {
if (getCursorY() == lines.size() - 1) return;
changeCursorY(1);
setCursorX(0);
}
else changeCursorX(1);
updateScroll();
}
public void left(boolean words) {
if (getCursorX() == 0) {
if (getCursorY() == 0) return;
changeCursorY(-1);
setCursorX(getLine().size());
}
else if (words) {
var line = getLine();
if (line.get(getCursorX() - 1) == ' ') {
for (; getCursorX() > 0; changeCursorX(-1)) {
if (line.get(getCursorX() - 1) != ' ') break;
}
}
else {
for (; getCursorX() > 0; changeCursorX(-1)) {
if (line.get(getCursorX() - 1) == ' ') break;
}
for (; getCursorX() > 0; changeCursorX(-1)) {
if (line.get(getCursorX() - 1) != ' ') break;
}
}
}
else changeCursorX(-1);
updateScroll();
}
@Override
public Widget setRect(int x, int y, int w, int h) {
super.setRect(x, y, w, h);
updateScroll();
return this;
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
try {
boolean ctrl = (modifiers & GLFW.GLFW_MOD_CONTROL) != 0;
switch (keyCode) {
case GLFW.GLFW_KEY_BACKSPACE: backspace(); return true;
case GLFW.GLFW_KEY_DELETE: delete(); return true;
case GLFW.GLFW_KEY_ENTER: newLine(); return true;
case GLFW.GLFW_KEY_TAB: tab(); return true;
case GLFW.GLFW_KEY_UP: changeCursorY(-1); return true;
case GLFW.GLFW_KEY_DOWN: changeCursorY(1); return true;
case GLFW.GLFW_KEY_LEFT: left(ctrl); return true;
case GLFW.GLFW_KEY_RIGHT: right(ctrl); return true;
case GLFW.GLFW_KEY_END: end(); return true;
case GLFW.GLFW_KEY_HOME: home(); return true;
case GLFW.GLFW_KEY_PAGE_DOWN: {
if (ctrl) changeScrollX(w / 2);
else changeScrollY(h / 2);
return true;
}
case GLFW.GLFW_KEY_PAGE_UP: {
if (ctrl) changeScrollX(-w / 2);
else changeScrollY(-h / 2);
return true;
}
}
}
catch (Throwable e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean charTyped(char c, int modifiers) {
if (c == '\n') newLine();
input(c);
return true;
}
@Override
public boolean mouseClicked(double mx, double my, int button) {
if (!isMouseOver(mx, my)) return false;
mx -= x - xScroll + theme.get("padding-s");
my -= y - yScroll + theme.get("padding-s");
setCursorY((int)Math.floor(my / fontHeight()));
setCursorX((int)Math.round(mx / fontWidth()));
return true;
}
@Override
public boolean mouseScrolled(double mx, double my, double amount) {
if (!isMouseOver(mx, my)) return false;
changeScrollY(-(float)amount * 10);
return true;
}
@Override
public void render(MatrixStack mat, int mouseX, int mouseY, float delta) {
renderSetup();
mat.push();
mat.translate(x, y, 0);
var width = 6;
var height = theme.fontSize();
fill(mat, 0, 0, this.w, this.h, theme.get("bg-1"));
mat.push();
mat.translate(theme.get("padding-s") - xScroll, theme.get("padding-s") - yScroll, 0);
for (int i = 0; i < lines.size(); i++) {
var line = lines.get(i);
for (int j = 0; j < line.size(); j++) {
var c = line.get(j);
float offX = (width - theme.font.getWidth(c + "")) / 2f;
theme.font.draw(mat, c + "", j * width + offX, i * height, theme.get("text"));
}
}
drawVerticalLine(mat, xPos * width, yPos * height - 1, (yPos + 1) * height, Argb.getArgb(255, 255, 0, 0));
mat.pop();
mat.pop();
renderFinalize();
super.render(mat, mouseX, mouseY, delta);
}
protected void textChanged() { }
@Override
public void appendNarrations(NarrationMessageBuilder nmb) {
nmb.put(NarrationPart.USAGE, "Text box");
}
@Override
public SelectionType getType() {
return SelectionType.NONE;
}
public TextBoxWidget(Theme theme) {
super(theme);
this.h = theme.fontSize() + theme.get("padding-s") * 2;
this.setValue("");
updateScroll();
}
}

View File

@ -0,0 +1,53 @@
package me.topchetoeu.mcscript.gui;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.util.math.ColorHelper.Argb;
public class Theme {
public final Map<String, Integer> colors = new HashMap<>();
public TextRenderer font;
public int fontSize() { return font.fontHeight; }
public Theme add(String name, int col) {
colors.put(name, col);
return this;
}
public Theme add(String name, int r, int g, int b, int a) {
colors.put(name, Argb.getArgb(a, r, g, b));
return this;
}
public int get(String name) {
var res = colors.get(name);
if (res == null) return 0;
else return res;
}
public Theme setTheme(Theme t) {
if (t == this) return this;
colors.clear();
colors.putAll(colors);
return this;
}
public Theme setFont(TextRenderer font) {
this.font = font;
return this;
}
public static Theme dark(TextRenderer font) {
return new Theme()
.setFont(font)
.add("padding-s", 2)
.add("padding-m", 3)
.add("padding-l", 5)
.add("bg-1", 0, 0, 0, 255)
.add("bg-2", 40, 40, 40, 255)
.add("bg-3", 80, 80, 80, 255)
.add("bg-4", 100, 100, 100, 255)
.add("border", 255, 255, 255, 255)
.add("text", 255, 255, 255, 255);
}
}

View File

@ -0,0 +1,101 @@
package me.topchetoeu.mcscript.gui;
import org.joml.Vector4f;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Drawable;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.Selectable;
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
import net.minecraft.client.util.math.MatrixStack;
public abstract class Widget extends DrawableHelper implements Element, Drawable, Selectable {
public final Theme theme;
protected int x, y, w, h;
protected abstract boolean hasBorders();
public final int getX() { return x; }
public final Widget setX(int val) { return setPos(val, y); }
public final int getY() { return y; }
public final Widget setY(int val) { return setPos(x, val); }
public final int getWidth() { return w; }
public final Widget setWidth(int value) { return setSize(value, h); }
public final int getHeight() { return h; }
public final Widget setHeight(int value) { return setSize(w, value); }
public final Widget setPos(int x, int y) {
return setRect(x, y, w, h);
}
public final Widget setSize(int w, int h) {
return setRect(x, y, w, h);
}
public Widget setRect(int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
return this;
}
@Override
public void setFocused(boolean focused) {
}
@Override
public boolean isFocused() {
return false;
}
@Override
public boolean isMouseOver(double mx, double my) {
return mx >= x && mx < x + w && my >= y && my < y + h;
}
@Override
public void appendNarrations(NarrationMessageBuilder var1) {
}
@Override
public SelectionType getType() {
return SelectionType.NONE;
}
protected void renderSetup() {
var m = RenderSystem.getProjectionMatrix();
var pos = m.transform(new Vector4f(x, y, 0, 1));
var scale = m.transform(new Vector4f(w, h, 0, 0));
int wndw = MinecraftClient.getInstance().getWindow().getWidth();
int wndh = MinecraftClient.getInstance().getWindow().getHeight();
int x = (int)Math.round((pos.x + 1) / 2 * wndw);
int y = (int)Math.round((-pos.y + 1) / 2 * wndh);
int w = (int)Math.round(scale.x / 2 * wndw);
int h = (int)Math.round(-scale.y / 2 * wndh);
y = wndh - y - h;
RenderSystem.enableScissor(x, y, w, h);
}
protected void renderFinalize() {
RenderSystem.disableScissor();
}
@Override
public void render(MatrixStack mat, int mx, int my, float delta) {
int col = theme.get("border");
drawHorizontalLine(mat, x - 1, x + w, y - 1, col);
drawHorizontalLine(mat, x - 1, x + w, y + h, col);
drawVerticalLine(mat, x - 1, y - 1, y + h, col);
drawVerticalLine(mat, x + w, y - 1, y + h, col);
}
public Widget(Theme theme) {
this.theme = theme;
}
}

View File

@ -0,0 +1,5 @@
package me.topchetoeu.mcscript.loader;
public interface ModLoader {
}

View File

@ -0,0 +1,37 @@
package me.topchetoeu.mcscript.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import me.topchetoeu.mcscript.events.ChatMessageCallback;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ChatScreen;
@Mixin(ChatScreen.class)
public abstract class ChatScreenMixin {
@Inject(method = "sendMessage", at = @At(value = "INVOKE", target = "Ljava/lang/String;startsWith(Ljava/lang/String;)Z"), cancellable = true)
private void onSendMessage(String chatText, boolean addToHistory, CallbackInfoReturnable<Boolean> cbi) {
var client = MinecraftClient.getInstance();
var args = new ChatMessageCallback.ChatArgs();
args.message = chatText;
args.cancelled = false;
ChatMessageCallback.EVENT.invoker().execute(args);
if (!args.cancelled) {
chatText = args.message;
if (chatText.startsWith("/")) {
client.player.networkHandler.sendChatCommand(chatText.substring(1));
} else {
client.player.networkHandler.sendChatMessage(chatText);
}
}
cbi.setReturnValue(true);
cbi.cancel();
}
}

View File

@ -0,0 +1,22 @@
package me.topchetoeu.mcscript.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.Keyboard;
import net.minecraft.client.MinecraftClient;
@Mixin(Keyboard.class)
public class KeyboardMixin {
@Inject(method = "onChar", at = @At("HEAD"))
private void onChar(long window, int codePoint, int modifiers, CallbackInfo cbi) {
var client = MinecraftClient.getInstance();
for (var ch : Character.toChars(codePoint)) {
if (client.currentScreen == null && client.getOverlay() == null && ch == '#') {
((MinecraftClientMixin)client).invokeOpenChatScreen("");
}
}
}
}

View File

@ -0,0 +1,12 @@
package me.topchetoeu.mcscript.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
import net.minecraft.client.MinecraftClient;
@Mixin(MinecraftClient.class)
public interface MinecraftClientMixin {
@Invoker
void invokeOpenChatScreen(String text);
}