diff --git a/src/main/java/dan200/computercraft/ComputerCraft.java b/src/main/java/dan200/computercraft/ComputerCraft.java index 3b1ce7495f..d735390eae 100644 --- a/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/src/main/java/dan200/computercraft/ComputerCraft.java @@ -154,6 +154,9 @@ public class ComputerCraft public static int maxNotesPerTick = 8; + public static int worldfsLimit = floppySpaceLimit; + public static boolean worldfs_enable = false; + // Blocks and Items public static class Blocks { @@ -223,6 +226,9 @@ public static class Config { public static Property maximumFilesOpen; public static Property maxNotesPerTick; + public static Property worldfsLimit; + public static Property worldfs_enable; + } // Registries @@ -344,6 +350,12 @@ public void preInit( FMLPreInitializationEvent event ) Config.maxNotesPerTick = Config.config.get( Configuration.CATEGORY_GENERAL, "maxNotesPerTick", maxNotesPerTick ); Config.maxNotesPerTick.setComment( "Maximum amount of notes a speaker can play at once" ); + Config.worldfs_enable = Config.config.get( Configuration.CATEGORY_GENERAL, "worldfs_enable", worldfs_enable ); + Config.worldfs_enable.setComment( "Enable the \"worldfs\" API " ); + + Config.worldfsLimit = Config.config.get( Configuration.CATEGORY_GENERAL, "worldfsLimit", worldfsLimit ); + Config.worldfsLimit.setComment( "The worldfs space limit for a worldfs channel, in bytes" ); + for (Property property : Config.config.getCategory( Configuration.CATEGORY_GENERAL ).getOrderedValues()) { property.setLanguageKey( "gui.computercraft:config." + CaseFormat.LOWER_CAMEL.to( CaseFormat.LOWER_UNDERSCORE, property.getName() ) ); @@ -387,6 +399,9 @@ public static void syncConfig() { maxNotesPerTick = Math.max(1, Config.maxNotesPerTick.getInt()); + worldfs_enable = Config.worldfs_enable.getBoolean(); + worldfsLimit = Config.worldfsLimit.getInt(); + Config.config.save(); } diff --git a/src/main/java/dan200/computercraft/core/apis/WorldFSAPI.java b/src/main/java/dan200/computercraft/core/apis/WorldFSAPI.java new file mode 100644 index 0000000000..35fd61c840 --- /dev/null +++ b/src/main/java/dan200/computercraft/core/apis/WorldFSAPI.java @@ -0,0 +1,110 @@ +package dan200.computercraft.core.apis; + +import dan200.computercraft.api.lua.ILuaContext; +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.core.filesystem.FileSystemException; +import dan200.computercraft.core.filesystem.WorldFSWrapper; +import static dan200.computercraft.core.apis.ArgumentHelper.getString; + +import java.util.HashMap; +import java.util.Map; + +public class WorldFSAPI implements ILuaAPI { + + private Map mounted_wfs; + private IAPIEnvironment m_api_enviroment; + + public WorldFSAPI(IAPIEnvironment iapiEnvironment) { + m_api_enviroment = iapiEnvironment; + mounted_wfs = new HashMap<>(); + } + + private void mount(String label, String channel) throws LuaException{ + if (!mounted_wfs.containsKey(channel)) { + WorldFSWrapper worldFSWrapper = new WorldFSWrapper(label, channel, m_api_enviroment.getFileSystem(), m_api_enviroment.getComputerEnvironment()); + mounted_wfs.put(channel, worldFSWrapper); + try { + worldFSWrapper.mount(); + } catch (FileSystemException fse) { + throw new LuaException("Error on mounting: "+fse.getMessage()); + } + } + else { + throw new LuaException( + String.format("Error worldfs %s is already mounted under %s", channel, mounted_wfs.get(channel).getPath()) + ); + } + } + + private void unmount(String channel) throws LuaException { + if (mounted_wfs.containsKey(channel)) { + mounted_wfs.get(channel).unmount(); + mounted_wfs.remove(channel); + } + else { + throw new LuaException( + String.format("%s is not mounted", channel) + ); + } + } + + @Override + public String[] getNames() { + return new String[]{ + "worldfs" + }; + } + + @Override + public void startup() { + mounted_wfs.clear(); + } + + @Override + public void advance(double _dt) { + + } + + @Override + public void shutdown() { + for (WorldFSWrapper mount : mounted_wfs.values()) { + mount.unmount(); + } + mounted_wfs.clear(); + } + + @Override + public String[] getMethodNames() { + return new String[]{ + "mount", + "unmount", + "list" + }; + } + + @Override + public Object[] callMethod(ILuaContext context, int method, Object[] arguments) throws LuaException, InterruptedException { + switch (method) { + case 0: // worldfs.mount(string, string) + String channel = getString(arguments, 0); + String label = getString(arguments, 1); + + mount(label, channel); + break; + case 1: // worldfs.unmount(string) + String channel2 = getString(arguments, 0); + + unmount(channel2); + break; + case 2: // worldfs.list() + Map list = new HashMap<>(); + int i=1; + for (WorldFSWrapper wrapper : mounted_wfs.values()) { + list.put(i++, wrapper); + } + return new Object[]{list}; + } + + return null; + } +} diff --git a/src/main/java/dan200/computercraft/core/computer/Computer.java b/src/main/java/dan200/computercraft/core/computer/Computer.java index 18e3751f8f..0826bc0c69 100644 --- a/src/main/java/dan200/computercraft/core/computer/Computer.java +++ b/src/main/java/dan200/computercraft/core/computer/Computer.java @@ -617,6 +617,9 @@ private void createAPIs() { m_apis.add( new HTTPAPI( m_apiEnvironment ) ); } + if (ComputerCraft.worldfs_enable) { + m_apis.add( new WorldFSAPI( m_apiEnvironment ) ); + } } private void initLua() diff --git a/src/main/java/dan200/computercraft/core/filesystem/WorldFSWrapper.java b/src/main/java/dan200/computercraft/core/filesystem/WorldFSWrapper.java new file mode 100644 index 0000000000..f4a1546993 --- /dev/null +++ b/src/main/java/dan200/computercraft/core/filesystem/WorldFSWrapper.java @@ -0,0 +1,98 @@ +package dan200.computercraft.core.filesystem; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.api.filesystem.IWritableMount; +import dan200.computercraft.api.lua.ILuaContext; +import dan200.computercraft.api.lua.ILuaObject; +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.core.computer.IComputerEnvironment; +import dan200.computercraft.shared.computer.core.IComputer; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.List; + +public class WorldFSWrapper implements ILuaObject { + + // Chars to create the Key + private static final String CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + + private String m_channelname; + private String m_channelkey; + private String m_path; + private FileSystem m_fs; + private IComputerEnvironment m_computerEnvironment; + + public WorldFSWrapper(String label, String channel, FileSystem fs, IComputerEnvironment computerEnvironment) { + m_channelname = channel; + m_channelkey = nameToKey(channel); + m_path = label; + m_computerEnvironment = computerEnvironment; + m_fs = fs; + } + + public void mount() throws FileSystemException{ + m_fs.mountWritable(m_path, m_path, getWritableMount()); + } + + public void unmount() { + m_fs.unmount(m_path); + } + + public String getPath() { + return m_path; + } + + public IWritableMount getWritableMount() { + return m_computerEnvironment.createSaveDirMount("computer/worldfs/"+m_channelkey, ComputerCraft.worldfsLimit); + } + + private static String nameToKey(String name) { + StringBuilder stringBuilder = new StringBuilder(); + char[] keyChars = CHARS.toCharArray(); + + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(name.getBytes(StandardCharsets.UTF_8)); + + for (byte b : hash) { + stringBuilder.append( + keyChars[ Math.abs( b % keyChars.length )] + ); + } + } catch (NoSuchAlgorithmException no) { + // Then we have some other problems... + no.printStackTrace(); + } + + return stringBuilder.toString(); + } + + @Override + public String[] getMethodNames() { + return new String[]{ + "getChannel", + "getPath", + "getKey" + }; + } + + @Override + public Object[] callMethod(ILuaContext context, int method, Object[] arguments) throws LuaException, InterruptedException { + switch (method) { + case 0: // getChannel + return new Object[]{m_channelname}; + case 1: // getPath + return new Object[]{m_path}; + case 2: // getKey + return new Object[]{m_channelkey}; + } + return null; + } +} diff --git a/src/main/resources/assets/computercraft/lua/rom/help/changelog.txt b/src/main/resources/assets/computercraft/lua/rom/help/changelog.txt index 92a74b2e0f..6342f27f50 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/changelog.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/changelog.txt @@ -33,6 +33,7 @@ New Features in ComputerCraft 1.80: * Added speaker block, turtle upgrade, pocket upgrade, and peripheral api * Startup can now be a directory containing multiple startup files * Added .getLabel to the computer peripheral +* Added worldfs api to create global file shares New Features in ComputerCraft 1.79: diff --git a/src/main/resources/assets/computercraft/lua/rom/help/whatsnew.txt b/src/main/resources/assets/computercraft/lua/rom/help/whatsnew.txt index a8c37cf1be..6b7c970eab 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/whatsnew.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/whatsnew.txt @@ -33,5 +33,6 @@ New Features in ComputerCraft 1.80: * Added speaker block, turtle upgrade, pocket upgrade, and peripheral api * Startup can now be a directory containing multiple startup files * Added .getLabel to the computer peripheral +* Added worldfs api to create global file shares Type "help changelog" to see the full version history. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/worldfs.txt b/src/main/resources/assets/computercraft/lua/rom/help/worldfs.txt new file mode 100644 index 0000000000..668a4df8d5 --- /dev/null +++ b/src/main/resources/assets/computercraft/lua/rom/help/worldfs.txt @@ -0,0 +1,6 @@ +worldfs is a program to quickly access the worldfs methods + +ex: +worldfs list +worldfs mount +worldfs unmount \ No newline at end of file diff --git a/src/main/resources/assets/computercraft/lua/rom/help/worldfsapi.txt b/src/main/resources/assets/computercraft/lua/rom/help/worldfsapi.txt new file mode 100644 index 0000000000..b0d575b64e --- /dev/null +++ b/src/main/resources/assets/computercraft/lua/rom/help/worldfsapi.txt @@ -0,0 +1,5 @@ +worldfs api is an api to communicate with the worldfs. +Functions in the worldfs API: +worldfs.list() +worldfs.mount( [channelname], [mountpath] ) +worldfs.unmount( [channelname] ) \ No newline at end of file diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/worldfs/worldfs.lua b/src/main/resources/assets/computercraft/lua/rom/programs/worldfs/worldfs.lua new file mode 100644 index 0000000000..a16b64512e --- /dev/null +++ b/src/main/resources/assets/computercraft/lua/rom/programs/worldfs/worldfs.lua @@ -0,0 +1,48 @@ +local function printUsage() + print("Usage:") + print("worldfs list") + print("worldfs mount