Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

worldfs - shared drives between all computers #568

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/main/java/dan200/computercraft/ComputerCraft.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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() ) );
Expand Down Expand Up @@ -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();
}

Expand Down
110 changes: 110 additions & 0 deletions src/main/java/dan200/computercraft/core/apis/WorldFSAPI.java
Original file line number Diff line number Diff line change
@@ -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<String, WorldFSWrapper> 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<Object,Object> list = new HashMap<>();
int i=1;
for (WorldFSWrapper wrapper : mounted_wfs.values()) {
list.put(i++, wrapper);
}
return new Object[]{list};
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
worldfs is a program to quickly access the worldfs methods

ex:
worldfs list
worldfs mount <channelname> <mount_path>
worldfs unmount <channelname>
Original file line number Diff line number Diff line change
@@ -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] )
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
local function printUsage()
print("Usage:")
print("worldfs list")
print("worldfs mount <channel> <label>")
print("worldfs unmount <channel>")
end

if not worldfs then
printError( "worldfs requires worldfs API" )
printError( "Set worldfs_enable to true in ComputerCraft.cfg" )
return
end

local targs = {...}
if #targs < 1 then
printUsage()
return
end

if targs[1] == "list" then
local tlist = worldfs.list()
if #tlist == 0 then
print("No worldfs drives mounted")
return
end
local twrap = {
colors.yellow,
{"Channel", "Path", "Key"},
colors.white
}
for k,value in ipairs(tlist) do
local key = string.sub(value.getKey(), 1, 10) .. "..."
table.insert(twrap, {
value.getChannel(),
value.getPath(),
key
})
end
textutils.tabulate( unpack(twrap) )
elseif targs[1] == "mount" and #targs >= 3 then
worldfs.mount(targs[2], targs[3])
print("mounted worldfs " .. targs[2] .. " under /"..targs[3])
elseif targs[1] == "unmount" and #targs >= 2 then
worldfs.unmount(targs[2])
print("unmounted worldfs "..targs[2])
else
printUsage()
end
13 changes: 13 additions & 0 deletions src/main/resources/assets/computercraft/lua/rom/startup.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ end
if http then
sPath = sPath..":/rom/programs/http"
end
if worldfs then
sPath = sPath..":/rom/programs/worldfs"
end
shell.setPath( sPath )
help.setPath( "/rom/help" )

Expand Down Expand Up @@ -231,6 +234,16 @@ if turtle then
shell.setCompletionFunction( "rom/programs/turtle/unequip.lua", completeUnequip )
end

if worldfs then
local tWorldFsOptions = { "list", "mount", "unmount"}
local function completeWorldFs(shell, nIndex, sText)
if nIndex == 1 then
return completeMultipleChoice(sText, tWorldFsOptions)
end
end
shell.setCompletionFunction("rom/programs/worldfs/worldfs.lua", completeWorldFs)
end


-- Run autorun files
if fs.exists( "/rom/autorun" ) and fs.isDir( "/rom/autorun" ) then
Expand Down