From 8b0ec4358ae4a6f8d7368559061d9983b497b732 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 18 Oct 2026 17:55:37 +0000 Subject: [PATCH 1/2] Fix the scripts directory path in the info book The External Editing section said that the directories of all disks are in world/integratedscripting/, while they are in world/integratedscripting/scripting-disks/. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018MRyTdV3fSV5Q5NPGep4q9 --- src/main/resources/assets/integratedscripting/lang/en_us.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/integratedscripting/lang/en_us.json b/src/main/resources/assets/integratedscripting/lang/en_us.json index 63dd0056e..e5bcab5d8 100644 --- a/src/main/resources/assets/integratedscripting/lang/en_us.json +++ b/src/main/resources/assets/integratedscripting/lang/en_us.json @@ -198,7 +198,7 @@ "info_book.integratedscripting.advanced.external_editing": "External Editing", "info_book.integratedscripting.advanced.external_editing.text1": "If you have access to the file system on which your singleplayer or multiplayer game is running, you can access script files through any external editor. This allows you to create scripts using your favorite IDE (such as Vi, but definitely not Emacs).", - "info_book.integratedscripting.advanced.external_editing.text2": "Script files are accessible via the &oworld/integratedscripting/&r directory (see &osaves/&r for singleplayer worlds). In here, you will find directories for all &lScripting Disks&r by their id. Inside of each &lScripting Disk&r directory, you will find all script files stored in that disk.", + "info_book.integratedscripting.advanced.external_editing.text2": "Script files are accessible via the &oworld/integratedscripting/scripting-disks/&r directory (see &osaves/&r for singleplayer worlds). In here, you will find directories for all &lScripting Disks&r by their id. Inside of each &lScripting Disk&r directory, you will find all script files stored in that disk.", "info_book.integratedscripting.advanced.external_editing.text3": "You can safely modify these script files through an external editor, even if the game is running. After externally saving the file, they will be updated automatically in the game, and dependent variables will be updated as well.", "info_book.integratedscripting.advanced.external_editing.text4": "If your external editor supports this (modern IDEs do), modifying a script file through the &lScripting Terminal&r will also cause the files to be updated in your external editor.", From 120ad29dc3536cb2ff115d247abb4706ab140725 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 18 Oct 2026 17:58:01 +0000 Subject: [PATCH 2/2] Add a button to open the scripts folder of a disk Editing scripts in an external editor required finding the disk's folder inside the world save. In singleplayer, and when hosting a LAN world, a button below the script list now opens the folder of the selected disk, creating it if needed, and its tooltip shows the folder's path. It is hidden when playing on another server, as the folder is not on the player's own computer then. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018MRyTdV3fSV5Q5NPGep4q9 --- .../ContainerScreenTerminalScripting.java | 55 +++++++++++++++++- .../client/gui/image/ScriptImages.java | 1 + .../core/network/ScriptingData.java | 2 +- .../integratedscripting/lang/en_us.json | 2 + .../textures/gui/icons.png | Bin 1389 -> 1431 bytes .../info/scripting_info.xml | 1 + 6 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cyclops/integratedscripting/client/gui/container/ContainerScreenTerminalScripting.java b/src/main/java/org/cyclops/integratedscripting/client/gui/container/ContainerScreenTerminalScripting.java index 32e457e4b..b51c9cb93 100644 --- a/src/main/java/org/cyclops/integratedscripting/client/gui/container/ContainerScreenTerminalScripting.java +++ b/src/main/java/org/cyclops/integratedscripting/client/gui/container/ContainerScreenTerminalScripting.java @@ -14,6 +14,7 @@ import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.resources.sounds.SimpleSoundInstance; import net.minecraft.ChatFormatting; +import net.minecraft.Util; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Style; import net.minecraft.resources.ResourceLocation; @@ -22,6 +23,7 @@ import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import org.apache.commons.lang3.tuple.Pair; +import org.apache.logging.log4j.Level; import org.cyclops.cyclopscore.client.gui.component.WidgetScrollBar; import org.cyclops.cyclopscore.client.gui.component.button.ButtonText; import org.cyclops.cyclopscore.client.gui.component.input.WidgetArrowedListField; @@ -53,6 +55,8 @@ import javax.annotation.Nullable; import java.nio.file.InvalidPathException; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.List; @@ -88,6 +92,9 @@ public class ContainerScreenTerminalScripting extends ContainerScreenExtended toolbarEditButtons = Lists.newArrayList(); private ButtonIcon buttonUndo; private ButtonIcon buttonRedo; @@ -179,10 +186,24 @@ public int getTotalRows() { }); addRenderableWidget(textArea); - buttonCreateFile = new ButtonText(this.leftPos + 19, this.topPos + 222, 56, 10, Component.translatable("gui.integratedscripting.create_file"), Component.literal("+"), + // The scripts folder can only be opened if this client is running the server, such as in singleplayer + boolean canOpenFolder = Minecraft.getInstance().hasSingleplayerServer(); + buttonCreateFile = new ButtonText(this.leftPos + PATHS_X, this.topPos + 222, canOpenFolder ? PATHS_WIDTH - 12 : PATHS_WIDTH, 10, Component.translatable("gui.integratedscripting.create_file"), Component.literal("+"), (button) -> getMenu().createNewFile(), true); + buttonOpenFolder = null; + buttonOpenFolderDisk = Integer.MIN_VALUE; if (!focusMode) { addRenderableWidget(buttonCreateFile); + if (canOpenFolder) { + buttonOpenFolder = new ButtonIcon(this.leftPos + PATHS_X + PATHS_WIDTH - 10, this.topPos + 222, 10, 10, + Component.translatable("gui.integratedscripting.open_folder"), (button) -> { + this.openDiskFolder(); + // Run after the click is handled, as that focuses the button + Minecraft.getInstance().tell(() -> this.setFocused(null)); + }, ScriptImages.ACTION_OPEN_FOLDER); + addRenderableWidget(buttonOpenFolder); + this.updateOpenFolderButton(); + } } if (focusMode) { @@ -239,6 +260,37 @@ private ButtonIcon addToolbarButton(int x, int y, IImage image, KeyMapping keyMa return button; } + protected Path getDiskFolder(int disk) { + return IntegratedScripting._instance.scriptingData.getDiskPath(disk).toAbsolutePath().normalize(); + } + + private void updateOpenFolderButton() { + int disk = getMenu().getActiveDisk(); + if (this.buttonOpenFolder != null && disk != this.buttonOpenFolderDisk) { + this.buttonOpenFolderDisk = disk; + this.buttonOpenFolder.active = disk >= 0; + Component tooltip = Component.translatable("gui.integratedscripting.open_folder"); + if (disk >= 0) { + tooltip = tooltip.copy().append("\n").append(Component.literal(this.getDiskFolder(disk).toString()).withStyle(ChatFormatting.GRAY)); + } + this.buttonOpenFolder.setTooltip(Tooltip.create(tooltip)); + } + } + + private void openDiskFolder() { + int disk = getMenu().getActiveDisk(); + if (disk >= 0) { + Path path = this.getDiskFolder(disk); + try { + // The folder of a disk without scripts may not exist yet + Files.createDirectories(path); + } catch (IOException e) { + IntegratedScripting.clog(Level.ERROR, "Could not create the scripts folder " + path + ": " + e.getMessage()); + } + Util.getPlatform().openPath(path); + } + } + private void updateToolbar() { boolean scriptOpen = getMenu().getActiveScript() != null; for (ButtonIcon button : this.toolbarEditButtons) { @@ -252,6 +304,7 @@ private void updateToolbar() { protected void containerTick() { super.containerTick(); textArea.tick(); + this.updateOpenFolderButton(); this.updateToolbar(); // The matches change when the script is edited while searching this.updateSearchStatus(); diff --git a/src/main/java/org/cyclops/integratedscripting/client/gui/image/ScriptImages.java b/src/main/java/org/cyclops/integratedscripting/client/gui/image/ScriptImages.java index 0796b72ef..4a980017b 100644 --- a/src/main/java/org/cyclops/integratedscripting/client/gui/image/ScriptImages.java +++ b/src/main/java/org/cyclops/integratedscripting/client/gui/image/ScriptImages.java @@ -21,6 +21,7 @@ public class ScriptImages { public static final Image ACTION_FIND = new Image(ICONS, 16, 16, 8, 8); public static final Image ACTION_GOTO_LINE = new Image(ICONS, 24, 16, 8, 8); public static final Image ACTION_COMMENT = new Image(ICONS, 32, 16, 8, 8); + public static final Image ACTION_OPEN_FOLDER = new Image(ICONS, 40, 16, 8, 8); public static final Image ACTION_FOCUS_ENTER = new Image(ICONS, 48, 16, 8, 8); public static final Image ACTION_FOCUS_EXIT = new Image(ICONS, 56, 16, 8, 8); diff --git a/src/main/java/org/cyclops/integratedscripting/core/network/ScriptingData.java b/src/main/java/org/cyclops/integratedscripting/core/network/ScriptingData.java index eea57b951..b36962848 100644 --- a/src/main/java/org/cyclops/integratedscripting/core/network/ScriptingData.java +++ b/src/main/java/org/cyclops/integratedscripting/core/network/ScriptingData.java @@ -101,7 +101,7 @@ protected Path getDisksPath() { return rootPath.resolve("scripting-disks"); } - protected Path getDiskPath(int id) { + public Path getDiskPath(int id) { return getDisksPath().resolve(String.valueOf(id)); } diff --git a/src/main/resources/assets/integratedscripting/lang/en_us.json b/src/main/resources/assets/integratedscripting/lang/en_us.json index e5bcab5d8..83af42152 100644 --- a/src/main/resources/assets/integratedscripting/lang/en_us.json +++ b/src/main/resources/assets/integratedscripting/lang/en_us.json @@ -26,6 +26,7 @@ "gui.integratedscripting.disk": "Disk", "gui.integratedscripting.script": "Script", "gui.integratedscripting.create_file": "Create File", + "gui.integratedscripting.open_folder": "Open scripts folder", "gui.integratedscripting.find": "Find", "gui.integratedscripting.focus_mode.enter": "Enlarge editor", "gui.integratedscripting.focus_mode.exit": "Back to normal size (Escape)", @@ -201,6 +202,7 @@ "info_book.integratedscripting.advanced.external_editing.text2": "Script files are accessible via the &oworld/integratedscripting/scripting-disks/&r directory (see &osaves/&r for singleplayer worlds). In here, you will find directories for all &lScripting Disks&r by their id. Inside of each &lScripting Disk&r directory, you will find all script files stored in that disk.", "info_book.integratedscripting.advanced.external_editing.text3": "You can safely modify these script files through an external editor, even if the game is running. After externally saving the file, they will be updated automatically in the game, and dependent variables will be updated as well.", "info_book.integratedscripting.advanced.external_editing.text4": "If your external editor supports this (modern IDEs do), modifying a script file through the &lScripting Terminal&r will also cause the files to be updated in your external editor.", + "info_book.integratedscripting.advanced.external_editing.text5": "In singleplayer, the folder button below the script list of the &lScripting Terminal&r opens the directory of the selected disk.", "info_book.integratedscripting.advanced.logging": "Logging", "info_book.integratedscripting.advanced.logging.text1": "For complex scripts, the need may arise to debug them by temporarily writing to external log files.", diff --git a/src/main/resources/assets/integratedscripting/textures/gui/icons.png b/src/main/resources/assets/integratedscripting/textures/gui/icons.png index 26a3a8cae54a382f803f80dff4e3a8d1b239011f..930ed750296ede14d5e713e1887eab87e77b6892 100644 GIT binary patch literal 1431 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJFNI8PVHkcv5P?;I?=9L3-e zXgQf_&tj2~Cand$+ol|tW>&}gj$!+ubFx43_w@v%9IrVvq2TFN6B<^O(seEjsAMc?y!yS=-2{%=2j zzwYBx#{HKM8{9GfIsM(tKL=iaPx$0~{qX!c#sl*|pJt4Re_sFN&)Kc^dj$K>zc1R) zKYu^39{+>46&Jl5fMyhbX*Yjz^g4qJ!@a(<3}@bdUth0x_WG~W!T;9g9(cWVC!4{3 z-5vjSZZSRSTT>BT_Wu6<`frc->_6n*`0(p3-TmAQTYe}Av~V(j(B2bFcVCw5(ouQy z{rB!;Hgo-+f8BRFH~Rbi-*>KW%e}je&FSV-o&?Lxd7JN)&-1x#!h6}|u1tFE{cG|f zk$KX`Kh{{y)%%~fcUk5vz5cyoeP3^EKi1sTp7_Jgus$tFNBs%V*N;E$n0^1#ZnJZB z`|rojKJi`V?{ve6K4ag@1r{=T`IhUB->N!y_~D1(i_QBlJ}wfH;cHKnXgj<3#kG{3 z=}fWJcYxZ~Ivcj9NVI+a^v%TA|Cp@|-(ics>4necAAel9{f=4x?wU8BcJ8nH`|D0U z(~Cb9Ey|Uh_}uezyhdB*thUD=E$sgZ^*wEg{$B^Qb2?Y_v81H=CQekSJ`3=X4cGz5l92;A8JdKTlNo~6NY Qzyg=S)78&qol`;+0NW5#{r~^~ literal 1389 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJD%6Hgb%kcv5P@0=~X6~)jH z_^^=iO^{bela`0{&1Ee)myfM(;B(Txxs&N$TVbDk?d$HUB4m{@G@DgNPI zi52hWLYex1g$F-Y+}T@F{b1Xj^XA72ZEAmidwa3hzW!g$KHI*>FX!va%iaBV`MUi2 zn(71ZOU!w$`~F*+t!7{F`<+kSi(S7j=QI2$j+1TpTKRAJdiy!Q4_g=h|DtyN_m{87 zAADiB*Kcmi`s4X&_Uify_j@(G4lED;-rza#r}F==s!Y4<@p7N?@3P-nchzV@y6_uRYN*qmpT89@85~{mn?q&tt;0Q(7*Zn z{IX0bzV^dEm5+aoh+bc5Au~_Z_J{Ocef5J63nbbSZ`|q5^PQHhc46uBx8Hx?wGt`z zXiK!v&9l#vOW$WF*T4H|?dKiQ=Ox<`fBgIVJ6NBwetV_9W#8kUo9xcWxBXYU^ykK& zzn^|i?^C(<*v{;qcb{9_;}WZ#J5uz&AO7%q;m@S?zfZ?iY<0Kuy<9TS<+SZ9n{##h z?d|v3natn5*WlmP>i!F9eVoncjt@N4fp3kYiS7zz?+_Zsv@|!<< zzCNityYqil&r_ubN-n!C!%i>ms=O$4>YP1Wl+TUNM{dpt2(P_kp1R}f5x!&=UHS4Rqf7=;$-+f&C<=TDwJDXinfo_book.integratedscripting.advanced.external_editing.text2 info_book.integratedscripting.advanced.external_editing.text3 info_book.integratedscripting.advanced.external_editing.text4 + info_book.integratedscripting.advanced.external_editing.text5